comparison src/c/urweb.c @ 2302:ace43b90b388

Tiny concurrency bugfix (race condition on cache->timeNow).
author Ziv Scully <ziv@mit.edu>
date Fri, 20 Nov 2015 10:51:43 -0500
parents 6e580e319077
children 42079884e34a
comparison
equal deleted inserted replaced
2301:8d772fbf59c1 2302:ace43b90b388
4615 uw_Sqlcache_delete(cache, cache->table); 4615 uw_Sqlcache_delete(cache, cache->table);
4616 } 4616 }
4617 } 4617 }
4618 4618
4619 static unsigned long uw_Sqlcache_getTimeNow(uw_Sqlcache_Cache *cache) { 4619 static unsigned long uw_Sqlcache_getTimeNow(uw_Sqlcache_Cache *cache) {
4620 return ++cache->timeNow; 4620 // TODO: verify that this makes time comparisons do the Right Thing.
4621 return cache->timeNow++;
4621 } 4622 }
4622 4623
4623 static unsigned long uw_Sqlcache_timeMax(unsigned long x, unsigned long y) { 4624 static unsigned long uw_Sqlcache_timeMax(unsigned long x, unsigned long y) {
4624 return x > y ? x : y; 4625 return x > y ? x : y;
4625 } 4626 }
4687 pthread_rwlock_unlock(&cache->lockIn); 4688 pthread_rwlock_unlock(&cache->lockIn);
4688 // ASK: though the argument isn't trivial, this is safe, right? 4689 // ASK: though the argument isn't trivial, this is safe, right?
4689 // Returning outside the lock is safe because updates happen at commit time. 4690 // Returning outside the lock is safe because updates happen at commit time.
4690 // Those are the only times the returned value or its strings can get freed. 4691 // Those are the only times the returned value or its strings can get freed.
4691 // Handler output is a new string, so it's safe to free this at commit time. 4692 // Handler output is a new string, so it's safe to free this at commit time.
4692 return value && value->timeValid > timeInvalid ? value : NULL; 4693 return value && timeInvalid < value->timeValid ? value : NULL;
4693 } 4694 }
4694 4695
4695 static void uw_Sqlcache_storeCommitOne(uw_Sqlcache_Cache *cache, char **keys, uw_Sqlcache_Value *value) { 4696 static void uw_Sqlcache_storeCommitOne(uw_Sqlcache_Cache *cache, char **keys, uw_Sqlcache_Value *value) {
4696 pthread_rwlock_wrlock(&cache->lockIn); 4697 pthread_rwlock_wrlock(&cache->lockIn);
4697 size_t numKeys = cache->numKeys; 4698 size_t numKeys = cache->numKeys;
4710 char *key = uw_Sqlcache_allocKeyBuffer(keys, numKeys); 4711 char *key = uw_Sqlcache_allocKeyBuffer(keys, numKeys);
4711 char *buf = key; 4712 char *buf = key;
4712 while (numKeys-- > 0) { 4713 while (numKeys-- > 0) {
4713 buf = uw_Sqlcache_keyCopy(buf, keys[numKeys]); 4714 buf = uw_Sqlcache_keyCopy(buf, keys[numKeys]);
4714 size_t len = buf - key; 4715 size_t len = buf - key;
4715 4716
4716 entry = uw_Sqlcache_find(cache, key, len, 1); 4717 entry = uw_Sqlcache_find(cache, key, len, 1);
4717 if (!entry) { 4718 if (!entry) {
4718 entry = calloc(1, sizeof(uw_Sqlcache_Entry)); 4719 entry = calloc(1, sizeof(uw_Sqlcache_Entry));
4719 entry->key = strdup(key); 4720 entry->key = strdup(key);
4720 entry->value = NULL; 4721 entry->value = NULL;
4811 uw_Sqlcache_Update *update = malloc(sizeof(uw_Sqlcache_Update)); 4812 uw_Sqlcache_Update *update = malloc(sizeof(uw_Sqlcache_Update));
4812 update->cache = cache; 4813 update->cache = cache;
4813 update->keys = uw_Sqlcache_copyKeys(keys, cache->numKeys); 4814 update->keys = uw_Sqlcache_copyKeys(keys, cache->numKeys);
4814 update->value = value; 4815 update->value = value;
4815 update->next = NULL; 4816 update->next = NULL;
4816 value->timeValid = uw_Sqlcache_getTimeNow(cache); 4817 // Can't use [uw_Sqlcache_getTimeNow] because it modifies state and we don't have the lock.
4818 value->timeValid = cache->timeNow;
4817 if (ctx->cacheUpdateTail) { 4819 if (ctx->cacheUpdateTail) {
4818 ctx->cacheUpdateTail->next = update; 4820 ctx->cacheUpdateTail->next = update;
4819 } else { 4821 } else {
4820 ctx->cacheUpdate = update; 4822 ctx->cacheUpdate = update;
4821 } 4823 }