comparison src/c/urweb.c @ 2288:98f96a976ede

Finish locking, but it's not yet tested rigorously.
author Ziv Scully <ziv@mit.edu>
date Fri, 13 Nov 2015 11:03:09 -0500
parents 08203f93dbc3
children 50ad02829abd
comparison
equal deleted inserted replaced
2287:08203f93dbc3 2288:98f96a976ede
364 void uw_global_init() { 364 void uw_global_init() {
365 clients = malloc(0); 365 clients = malloc(0);
366 366
367 uw_global_custom(); 367 uw_global_custom();
368 uw_init_crypto(); 368 uw_init_crypto();
369
370 // Fast non-cryptographic strength randomness for Sqlcache.
371 srandom(clock());
369 } 372 }
370 373
371 void uw_app_init(uw_app *app) { 374 void uw_app_init(uw_app *app) {
372 app->client_init(); 375 app->client_init();
373 } 376 }
429 char **keys; 432 char **keys;
430 uw_Sqlcache_Value *value; 433 uw_Sqlcache_Value *value;
431 struct uw_Sqlcache_Update *next; 434 struct uw_Sqlcache_Update *next;
432 } uw_Sqlcache_Update; 435 } uw_Sqlcache_Update;
433 436
437 typedef struct uw_Sqlcache_Unlock {
438 pthread_rwlock_t *lock;
439 struct uw_Sqlcache_Unlock *next;
440 } uw_Sqlcache_Unlock;
441
434 struct uw_context { 442 struct uw_context {
435 uw_app *app; 443 uw_app *app;
436 int id; 444 int id;
437 445
438 char *(*get_header)(void *, const char *); 446 char *(*get_header)(void *, const char *);
498 // Sqlcache. 506 // Sqlcache.
499 int numRecording; 507 int numRecording;
500 int recordingOffset; 508 int recordingOffset;
501 uw_Sqlcache_Update *cacheUpdate; 509 uw_Sqlcache_Update *cacheUpdate;
502 uw_Sqlcache_Update *cacheUpdateTail; 510 uw_Sqlcache_Update *cacheUpdateTail;
511 uw_Sqlcache_Unlock *cacheUnlock;
503 512
504 int remoteSock; 513 int remoteSock;
505 }; 514 };
506 515
507 size_t uw_headers_max = SIZE_MAX; 516 size_t uw_headers_max = SIZE_MAX;
4554 uw_Sqlcache_Value *value; 4563 uw_Sqlcache_Value *value;
4555 unsigned long timeInvalid; 4564 unsigned long timeInvalid;
4556 UT_hash_handle hh; 4565 UT_hash_handle hh;
4557 } uw_Sqlcache_Entry; 4566 } uw_Sqlcache_Entry;
4558 4567
4559 void uw_Sqlcache_freeValue(uw_Sqlcache_Value *value) { 4568 static void uw_Sqlcache_freeValue(uw_Sqlcache_Value *value) {
4560 if (value) { 4569 if (value) {
4561 free(value->result); 4570 free(value->result);
4562 free(value->output); 4571 free(value->output);
4563 free(value); 4572 free(value);
4564 } 4573 }
4565 } 4574 }
4566 4575
4567 void uw_Sqlcache_freeEntry(uw_Sqlcache_Entry* entry) { 4576 static void uw_Sqlcache_freeEntry(uw_Sqlcache_Entry* entry) {
4568 if (entry) { 4577 if (entry) {
4569 free(entry->key); 4578 free(entry->key);
4570 uw_Sqlcache_freeValue(entry->value); 4579 uw_Sqlcache_freeValue(entry->value);
4571 free(entry); 4580 free(entry);
4572 } 4581 }
4573 } 4582 }
4574 4583
4575 // TODO: pick a number. 4584 // TODO: pick a number.
4576 unsigned int uw_Sqlcache_maxSize = 1234567890; 4585 static unsigned int uw_Sqlcache_maxSize = 1234567890;
4577 4586
4578 void uw_Sqlcache_delete(uw_Sqlcache_Cache *cache, uw_Sqlcache_Entry *entry) { 4587 static void uw_Sqlcache_delete(uw_Sqlcache_Cache *cache, uw_Sqlcache_Entry *entry) {
4579 HASH_DEL(cache->table, entry); 4588 HASH_DEL(cache->table, entry);
4580 uw_Sqlcache_freeEntry(entry); 4589 uw_Sqlcache_freeEntry(entry);
4581 } 4590 }
4582 4591
4583 uw_Sqlcache_Entry *uw_Sqlcache_find(uw_Sqlcache_Cache *cache, char *key, size_t len, int bump) { 4592 static uw_Sqlcache_Entry *uw_Sqlcache_find(uw_Sqlcache_Cache *cache, char *key, size_t len, int bump) {
4584 uw_Sqlcache_Entry *entry = NULL; 4593 uw_Sqlcache_Entry *entry = NULL;
4585 HASH_FIND(hh, cache->table, key, len, entry); 4594 HASH_FIND(hh, cache->table, key, len, entry);
4586 if (entry && bump) { 4595 if (entry && bump) {
4587 // Bump for LRU purposes. 4596 // Bump for LRU purposes.
4588 HASH_DEL(cache->table, entry); 4597 HASH_DEL(cache->table, entry);
4590 HASH_ADD_KEYPTR(hh, cache->table, entry->key, len, entry); 4599 HASH_ADD_KEYPTR(hh, cache->table, entry->key, len, entry);
4591 } 4600 }
4592 return entry; 4601 return entry;
4593 } 4602 }
4594 4603
4595 void uw_Sqlcache_add(uw_Sqlcache_Cache *cache, uw_Sqlcache_Entry *entry, size_t len) { 4604 static void uw_Sqlcache_add(uw_Sqlcache_Cache *cache, uw_Sqlcache_Entry *entry, size_t len) {
4596 HASH_ADD_KEYPTR(hh, cache->table, entry->key, len, entry); 4605 HASH_ADD_KEYPTR(hh, cache->table, entry->key, len, entry);
4597 if (HASH_COUNT(cache->table) > uw_Sqlcache_maxSize) { 4606 if (HASH_COUNT(cache->table) > uw_Sqlcache_maxSize) {
4598 // Deletes the first element of the cache. 4607 // Deletes the first element of the cache.
4599 uw_Sqlcache_delete(cache, cache->table); 4608 uw_Sqlcache_delete(cache, cache->table);
4600 } 4609 }
4601 } 4610 }
4602 4611
4603 unsigned long uw_Sqlcache_getTimeNow(uw_Sqlcache_Cache *cache) { 4612 static unsigned long uw_Sqlcache_getTimeNow(uw_Sqlcache_Cache *cache) {
4604 return ++cache->timeNow; 4613 return ++cache->timeNow;
4605 } 4614 }
4606 4615
4607 unsigned long uw_Sqlcache_timeMax(unsigned long x, unsigned long y) { 4616 static unsigned long uw_Sqlcache_timeMax(unsigned long x, unsigned long y) {
4608 return x > y ? x : y; 4617 return x > y ? x : y;
4609 } 4618 }
4610 4619
4611 char uw_Sqlcache_keySep = '_'; 4620 static char uw_Sqlcache_keySep = '_';
4612 4621
4613 char *uw_Sqlcache_allocKeyBuffer(char **keys, size_t numKeys) { 4622 static char *uw_Sqlcache_allocKeyBuffer(char **keys, size_t numKeys) {
4614 size_t len = 0; 4623 size_t len = 0;
4615 while (numKeys-- > 0) { 4624 while (numKeys-- > 0) {
4616 char* k = keys[numKeys]; 4625 char* k = keys[numKeys];
4617 if (!k) { 4626 if (!k) {
4618 // Can only happen when flushihg, in which case we don't need anything past the null key. 4627 // Can only happen when flushihg, in which case we don't need anything past the null key.
4625 // If nothing is copied into the buffer, it should look like it has length 0. 4634 // If nothing is copied into the buffer, it should look like it has length 0.
4626 buf[0] = 0; 4635 buf[0] = 0;
4627 return buf; 4636 return buf;
4628 } 4637 }
4629 4638
4630 char *uw_Sqlcache_keyCopy(char *buf, char *key) { 4639 static char *uw_Sqlcache_keyCopy(char *buf, char *key) {
4631 *buf++ = uw_Sqlcache_keySep; 4640 *buf++ = uw_Sqlcache_keySep;
4632 return stpcpy(buf, key); 4641 return stpcpy(buf, key);
4633 } 4642 }
4634 4643
4635 // The NUL-terminated prefix of [key] below always looks something like "_k1_k2_k3..._kn". 4644 // The NUL-terminated prefix of [key] below always looks something like "_k1_k2_k3..._kn".
4636 4645
4637 uw_Sqlcache_Value *uw_Sqlcache_check(uw_context ctx, uw_Sqlcache_Cache *cache, char **keys) { 4646 uw_Sqlcache_Value *uw_Sqlcache_check(uw_context ctx, uw_Sqlcache_Cache *cache, char **keys) {
4638 pthread_rwlock_rdlock(&cache->lock); 4647 int doBump = random() % 1024 == 0;
4648 if (doBump) {
4649 pthread_rwlock_wrlock(&cache->lockIn);
4650 } else {
4651 pthread_rwlock_rdlock(&cache->lockIn);
4652 }
4639 size_t numKeys = cache->numKeys; 4653 size_t numKeys = cache->numKeys;
4640 char *key = uw_Sqlcache_allocKeyBuffer(keys, numKeys); 4654 char *key = uw_Sqlcache_allocKeyBuffer(keys, numKeys);
4641 char *buf = key; 4655 char *buf = key;
4642 time_t timeInvalid = cache->timeInvalid; 4656 time_t timeInvalid = cache->timeInvalid;
4643 uw_Sqlcache_Entry *entry; 4657 uw_Sqlcache_Entry *entry;
4644 if (numKeys == 0) { 4658 if (numKeys == 0) {
4645 entry = cache->table; 4659 entry = cache->table;
4646 if (!entry) { 4660 if (!entry) {
4647 free(key); 4661 free(key);
4648 pthread_rwlock_unlock(&cache->lock); 4662 pthread_rwlock_unlock(&cache->lockIn);
4649 return NULL; 4663 return NULL;
4650 } 4664 }
4651 } else { 4665 } else {
4652 while (numKeys-- > 0) { 4666 while (numKeys-- > 0) {
4653 buf = uw_Sqlcache_keyCopy(buf, keys[numKeys]); 4667 buf = uw_Sqlcache_keyCopy(buf, keys[numKeys]);
4654 size_t len = buf - key; 4668 size_t len = buf - key;
4655 entry = uw_Sqlcache_find(cache, key, len, 1); 4669 entry = uw_Sqlcache_find(cache, key, len, doBump);
4656 if (!entry) { 4670 if (!entry) {
4657 free(key); 4671 free(key);
4658 pthread_rwlock_unlock(&cache->lock); 4672 pthread_rwlock_unlock(&cache->lockIn);
4659 return NULL; 4673 return NULL;
4660 } 4674 }
4661 timeInvalid = uw_Sqlcache_timeMax(timeInvalid, entry->timeInvalid); 4675 timeInvalid = uw_Sqlcache_timeMax(timeInvalid, entry->timeInvalid);
4662 } 4676 }
4663 free(key); 4677 free(key);
4664 } 4678 }
4665 // TODO: pass back copy of value and free it in the generated code... or use uw_malloc?
4666 uw_Sqlcache_Value *value = entry->value; 4679 uw_Sqlcache_Value *value = entry->value;
4667 pthread_rwlock_unlock(&cache->lock); 4680 pthread_rwlock_unlock(&cache->lockIn);
4681 // ASK: though the argument isn't trivial, this is safe, right?
4682 // Returning outside the lock is safe because updates happen at commit time.
4683 // Those are the only times the returned value or its strings can get freed.
4684 // Handler output is a new string, so it's safe to free this at commit time.
4668 return value && value->timeValid > timeInvalid ? value : NULL; 4685 return value && value->timeValid > timeInvalid ? value : NULL;
4669 } 4686 }
4670 4687
4671 void uw_Sqlcache_storeCommitOne(uw_Sqlcache_Cache *cache, char **keys, uw_Sqlcache_Value *value) { 4688 static void uw_Sqlcache_storeCommitOne(uw_Sqlcache_Cache *cache, char **keys, uw_Sqlcache_Value *value) {
4672 pthread_rwlock_wrlock(&cache->lock); 4689 pthread_rwlock_wrlock(&cache->lockIn);
4673 size_t numKeys = cache->numKeys; 4690 size_t numKeys = cache->numKeys;
4674 char *key = uw_Sqlcache_allocKeyBuffer(keys, numKeys);
4675 char *buf = key;
4676 time_t timeNow = uw_Sqlcache_getTimeNow(cache); 4691 time_t timeNow = uw_Sqlcache_getTimeNow(cache);
4677 uw_Sqlcache_Entry *entry; 4692 uw_Sqlcache_Entry *entry;
4678 if (numKeys == 0) { 4693 if (numKeys == 0) {
4679 entry = cache->table; 4694 entry = cache->table;
4680 if (!entry) { 4695 if (!entry) {
4681 entry = malloc(sizeof(uw_Sqlcache_Entry)); 4696 entry = malloc(sizeof(uw_Sqlcache_Entry));
4682 entry->key = strdup(key); 4697 entry->key = NULL;
4683 entry->value = NULL; 4698 entry->value = NULL;
4684 entry->timeInvalid = 0; 4699 entry->timeInvalid = 0;
4685 cache->table = entry; 4700 cache->table = entry;
4686 } 4701 }
4687 } else { 4702 } else {
4703 char *key = uw_Sqlcache_allocKeyBuffer(keys, numKeys);
4704 char *buf = key;
4688 while (numKeys-- > 0) { 4705 while (numKeys-- > 0) {
4689 buf = uw_Sqlcache_keyCopy(buf, keys[numKeys]); 4706 buf = uw_Sqlcache_keyCopy(buf, keys[numKeys]);
4690 size_t len = buf - key; 4707 size_t len = buf - key;
4691 entry = uw_Sqlcache_find(cache, key, len, 1); 4708 entry = uw_Sqlcache_find(cache, key, len, 1);
4692 if (!entry) { 4709 if (!entry) {
4700 free(key); 4717 free(key);
4701 } 4718 }
4702 uw_Sqlcache_freeValue(entry->value); 4719 uw_Sqlcache_freeValue(entry->value);
4703 entry->value = value; 4720 entry->value = value;
4704 entry->value->timeValid = timeNow; 4721 entry->value->timeValid = timeNow;
4705 pthread_rwlock_unlock(&cache->lock); 4722 pthread_rwlock_unlock(&cache->lockIn);
4706 } 4723 }
4707 4724
4708 void uw_Sqlcache_flushCommitOne(uw_Sqlcache_Cache *cache, char **keys) { 4725 static void uw_Sqlcache_flushCommitOne(uw_Sqlcache_Cache *cache, char **keys) {
4709 pthread_rwlock_wrlock(&cache->lock); 4726 pthread_rwlock_wrlock(&cache->lockIn);
4710 size_t numKeys = cache->numKeys; 4727 size_t numKeys = cache->numKeys;
4711 char *key = uw_Sqlcache_allocKeyBuffer(keys, numKeys);
4712 char *buf = key;
4713 time_t timeNow = uw_Sqlcache_getTimeNow(cache);
4714 uw_Sqlcache_Entry *entry;
4715 if (numKeys == 0) { 4728 if (numKeys == 0) {
4716 entry = cache->table; 4729 uw_Sqlcache_Entry *entry = cache->table;
4717 if (entry) { 4730 if (entry) {
4718 uw_Sqlcache_freeValue(entry->value); 4731 uw_Sqlcache_freeValue(entry->value);
4719 entry->value = NULL; 4732 entry->value = NULL;
4720 } 4733 }
4721 } else { 4734 } else {
4735 char *key = uw_Sqlcache_allocKeyBuffer(keys, numKeys);
4736 char *buf = key;
4737 time_t timeNow = uw_Sqlcache_getTimeNow(cache);
4738 uw_Sqlcache_Entry *entry = NULL;
4722 while (numKeys-- > 0) { 4739 while (numKeys-- > 0) {
4723 char *k = keys[numKeys]; 4740 char *k = keys[numKeys];
4724 if (!k) { 4741 if (!k) {
4725 if (entry) { 4742 if (entry) {
4726 entry->timeInvalid = timeNow; 4743 entry->timeInvalid = timeNow;
4727 } else { 4744 } else {
4728 // Haven't found an entry yet, so the first key was null. 4745 // Haven't found an entry yet, so the first key was null.
4729 cache->timeInvalid = timeNow; 4746 cache->timeInvalid = timeNow;
4730 } 4747 }
4731 free(key); 4748 free(key);
4732 pthread_rwlock_unlock(&cache->lock); 4749 pthread_rwlock_unlock(&cache->lockIn);
4733 return; 4750 return;
4734 } 4751 }
4735 buf = uw_Sqlcache_keyCopy(buf, k); 4752 buf = uw_Sqlcache_keyCopy(buf, k);
4736 size_t len = buf - key; 4753 size_t len = buf - key;
4737 entry = uw_Sqlcache_find(cache, key, len, 0); 4754 entry = uw_Sqlcache_find(cache, key, len, 0);
4738 if (!entry) { 4755 if (!entry) {
4756 // Nothing in the cache to flush.
4739 free(key); 4757 free(key);
4740 pthread_rwlock_unlock(&cache->lock); 4758 pthread_rwlock_unlock(&cache->lockIn);
4741 return; 4759 return;
4742 } 4760 }
4743 } 4761 }
4744 free(key); 4762 free(key);
4745 // All the keys were non-null and the relevant entry is present, so we delete it. 4763 // All the keys were non-null and the relevant entry is present, so we delete it.
4746 uw_Sqlcache_delete(cache, entry); 4764 uw_Sqlcache_delete(cache, entry);
4747 } 4765 }
4748 pthread_rwlock_unlock(&cache->lock); 4766 pthread_rwlock_unlock(&cache->lockIn);
4749 } 4767 }
4750 4768
4751 void uw_Sqlcache_freeUpdate(void *data, int dontCare) { 4769 static void uw_Sqlcache_commit(void *data) {
4752 uw_context ctx = (uw_context)data;
4753 uw_Sqlcache_Update *update = ctx->cacheUpdate;
4754 while (update) {
4755 char** keys = update->keys;
4756 size_t numKeys = update->cache->numKeys;
4757 while (numKeys-- > 0) {
4758 free(keys[numKeys]);
4759 }
4760 free(keys);
4761 // Don't free [update->value]: it's in the cache now!
4762 uw_Sqlcache_Update *nextUpdate = update->next;
4763 free(update);
4764 update = nextUpdate;
4765 }
4766 ctx->cacheUpdate = NULL;
4767 ctx->cacheUpdateTail = NULL;
4768 }
4769
4770 void uw_Sqlcache_commitUpdate(void *data) {
4771 uw_context ctx = (uw_context)data; 4770 uw_context ctx = (uw_context)data;
4772 uw_Sqlcache_Update *update = ctx->cacheUpdate; 4771 uw_Sqlcache_Update *update = ctx->cacheUpdate;
4773 while (update) { 4772 while (update) {
4774 uw_Sqlcache_Cache *cache = update->cache; 4773 uw_Sqlcache_Cache *cache = update->cache;
4775 char **keys = update->keys; 4774 char **keys = update->keys;
4780 } 4779 }
4781 update = update->next; 4780 update = update->next;
4782 } 4781 }
4783 } 4782 }
4784 4783
4785 char **uw_Sqlcache_copyKeys(char **keys, size_t numKeys) { 4784 static void uw_Sqlcache_free(void *data, int dontCare) {
4785 uw_context ctx = (uw_context)data;
4786 uw_Sqlcache_Update *update = ctx->cacheUpdate;
4787 while (update) {
4788 char** keys = update->keys;
4789 size_t numKeys = update->cache->numKeys;
4790 while (numKeys-- > 0) {
4791 free(keys[numKeys]);
4792 }
4793 free(keys);
4794 // Don't free [update->value]: it's in the cache now!
4795 uw_Sqlcache_Update *nextUpdate = update->next;
4796 free(update);
4797 update = nextUpdate;
4798 }
4799 ctx->cacheUpdate = NULL;
4800 ctx->cacheUpdateTail = NULL;
4801 uw_Sqlcache_Unlock *unlock = ctx->cacheUnlock;
4802 while (unlock) {
4803 pthread_rwlock_unlock(unlock->lock);
4804 uw_Sqlcache_Unlock *nextUnlock = unlock->next;
4805 free(unlock);
4806 unlock = nextUnlock;
4807 }
4808 ctx->cacheUnlock = NULL;
4809 }
4810
4811 static void uw_Sqlcache_pushUnlock(uw_context ctx, pthread_rwlock_t *lock) {
4812 if (!ctx->cacheUnlock) {
4813 // Just need one registered commit for both updating and unlocking.
4814 uw_register_transactional(ctx, ctx, uw_Sqlcache_commit, NULL, uw_Sqlcache_free);
4815 }
4816 uw_Sqlcache_Unlock *unlock = malloc(sizeof(uw_Sqlcache_Unlock));
4817 unlock->lock = lock;
4818 unlock->next = ctx->cacheUnlock;
4819 ctx->cacheUnlock = unlock;
4820 }
4821
4822 void uw_Sqlcache_rlock(uw_context ctx, uw_Sqlcache_Cache *cache) {
4823 pthread_rwlock_rdlock(&cache->lockOut);
4824 uw_Sqlcache_pushUnlock(ctx, &cache->lockOut);
4825 }
4826
4827 void uw_Sqlcache_wlock(uw_context ctx, uw_Sqlcache_Cache *cache) {
4828 pthread_rwlock_wrlock(&cache->lockOut);
4829 uw_Sqlcache_pushUnlock(ctx, &cache->lockOut);
4830 }
4831
4832 static char **uw_Sqlcache_copyKeys(char **keys, size_t numKeys) {
4786 char **copy = malloc(sizeof(char *) * numKeys); 4833 char **copy = malloc(sizeof(char *) * numKeys);
4787 while (numKeys-- > 0) { 4834 while (numKeys-- > 0) {
4788 char *k = keys[numKeys]; 4835 char *k = keys[numKeys];
4789 copy[numKeys] = k ? strdup(k) : NULL; 4836 copy[numKeys] = k ? strdup(k) : NULL;
4790 } 4837 }
4796 update->cache = cache; 4843 update->cache = cache;
4797 update->keys = uw_Sqlcache_copyKeys(keys, cache->numKeys); 4844 update->keys = uw_Sqlcache_copyKeys(keys, cache->numKeys);
4798 update->value = value; 4845 update->value = value;
4799 update->next = NULL; 4846 update->next = NULL;
4800 if (ctx->cacheUpdateTail) { 4847 if (ctx->cacheUpdateTail) {
4801 // An update is already registered, so just extend it.
4802 ctx->cacheUpdateTail->next = update; 4848 ctx->cacheUpdateTail->next = update;
4803 } else { 4849 } else {
4804 ctx->cacheUpdate = update; 4850 ctx->cacheUpdate = update;
4805 uw_register_transactional(ctx, ctx, uw_Sqlcache_commitUpdate, NULL, uw_Sqlcache_freeUpdate);
4806 } 4851 }
4807 ctx->cacheUpdateTail = update; 4852 ctx->cacheUpdateTail = update;
4808 } 4853 }
4809 4854
4810 void uw_Sqlcache_flush(uw_context ctx, uw_Sqlcache_Cache *cache, char **keys) { 4855 void uw_Sqlcache_flush(uw_context ctx, uw_Sqlcache_Cache *cache, char **keys) {