static void unload_cb(ErlNifEnv *env, void *priv_data) { struct atom_node *an; enif_rwlock_rwlock(gbl->atom_lock); /* when we unload, we want to tell all of the active caches to die, then join() their bg_threads to wait until they're completely gone */ while ((an = RB_MIN(atom_tree, &(gbl->atom_head)))) { struct cache *c = an->cache; enif_rwlock_rwunlock(gbl->atom_lock); enif_mutex_lock(c->ctrl_lock); c->flags |= FL_DYING; enif_mutex_unlock(c->ctrl_lock); enif_cond_broadcast(c->check_cond); enif_thread_join(c->bg_thread, NULL); enif_rwlock_rwlock(gbl->atom_lock); } enif_rwlock_rwunlock(gbl->atom_lock); enif_rwlock_destroy(gbl->atom_lock); enif_clear_env(gbl->atom_env); enif_free(gbl); gbl = NULL; }
/* destroy(Cache :: atom()) -- destroys and entire cache destroy(Cache :: atom(), Key :: binary()) -- removes an entry from a cache */ static ERL_NIF_TERM destroy(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM atom; struct cache *c; ErlNifBinary kbin; struct cache_node *n; if (!enif_is_atom(env, argv[0])) return enif_make_badarg(env); atom = argv[0]; if ((c = get_cache(atom))) { if (argc == 2) { if (!enif_inspect_binary(env, argv[1], &kbin)) return enif_make_badarg(env); enif_rwlock_rwlock(c->cache_lock); enif_rwlock_rwlock(c->lookup_lock); HASH_FIND(hh, c->lookup, kbin.data, kbin.size, n); if (!n) { enif_rwlock_rwunlock(c->lookup_lock); enif_rwlock_rwunlock(c->cache_lock); return enif_make_atom(env, "notfound"); } enif_mutex_lock(c->ctrl_lock); destroy_cache_node(n); enif_mutex_unlock(c->ctrl_lock); enif_rwlock_rwunlock(c->lookup_lock); enif_rwlock_rwunlock(c->cache_lock); enif_consume_timeslice(env, 50); return enif_make_atom(env, "ok"); } else { enif_mutex_lock(c->ctrl_lock); c->flags |= FL_DYING; enif_mutex_unlock(c->ctrl_lock); enif_cond_broadcast(c->check_cond); enif_thread_join(c->bg_thread, NULL); enif_consume_timeslice(env, 100); return enif_make_atom(env, "ok"); } return enif_make_atom(env, "ok"); } return enif_make_atom(env, "notfound"); }
static ERL_NIF_TERM Respond(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { TRACE("Respond\n"); int id; if(enif_get_int(env, argv[0], &id)) { ErlCall *erlCall = FindCall(id); if(erlCall) { enif_mutex_lock(erlCall->mutex); erlCall->result = argv[1]; erlCall->complete = 1; enif_cond_broadcast(erlCall->cond); enif_mutex_unlock(erlCall->mutex); return enif_make_atom(env, "ok"); } else { return enif_make_badarg(env); } } else { return enif_make_badarg(env); } }
static ERL_NIF_TERM get(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM atom; ErlNifBinary kbin; struct cache *c; struct cache_node *n; struct cache_incr_node *in; struct timespec now; int incrqs, hashv, bkt; ERL_NIF_TERM ret; ErlNifTid tid; if (!enif_is_atom(env, argv[0])) return enif_make_badarg(env); atom = argv[0]; if (!enif_inspect_binary(env, argv[1], &kbin)) return enif_make_badarg(env); if ((c = get_cache(atom))) { enif_rwlock_rlock(c->lookup_lock); HASH_FIND(hh, c->lookup, kbin.data, kbin.size, n); if (!n) { enif_rwlock_runlock(c->lookup_lock); __sync_add_and_fetch(&c->miss, 1); enif_consume_timeslice(env, 10); return enif_make_atom(env, "notfound"); } if (n->expiry.tv_sec != 0) { clock_now(&now); if (n->expiry.tv_sec < now.tv_sec) { enif_rwlock_runlock(c->lookup_lock); __sync_add_and_fetch(&c->miss, 1); enif_consume_timeslice(env, 10); return enif_make_atom(env, "notfound"); } } in = enif_alloc(sizeof(*in)); memset(in, 0, sizeof(*in)); in->node = n; __sync_add_and_fetch(&c->hit, 1); tid = enif_thread_self(); HASH_SFH(&tid, sizeof(ErlNifTid), N_INCR_BKT, hashv, bkt); enif_mutex_lock(c->incr_lock[bkt]); TAILQ_INSERT_TAIL(&(c->incr_head[bkt]), in, entry); enif_mutex_unlock(c->incr_lock[bkt]); incrqs = __sync_add_and_fetch(&(c->incr_count), 1); ret = enif_make_resource_binary(env, n->val, n->val, n->vsize); enif_rwlock_runlock(c->lookup_lock); if (incrqs > 1024) enif_cond_broadcast(c->check_cond); enif_consume_timeslice(env, 20); return ret; } return enif_make_atom(env, "notfound"); }
static ERL_NIF_TERM put(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM atom; ErlNifBinary kbin, vbin; struct cache *c; struct cache_node *n, *ng; ErlNifUInt64 lifetime = 0; if (!enif_is_atom(env, argv[0])) return enif_make_badarg(env); atom = argv[0]; if (!enif_inspect_binary(env, argv[1], &kbin)) return enif_make_badarg(env); if (!enif_inspect_binary(env, argv[2], &vbin)) return enif_make_badarg(env); if ((c = get_cache(atom))) { enif_consume_timeslice(env, 1); } else { /* if we've been asked to put() in to a cache that doesn't exist yet then we should create it! */ ErlNifUInt64 max_size, min_q1_size; if (!enif_get_uint64(env, argv[3], &max_size)) return enif_make_badarg(env); if (!enif_get_uint64(env, argv[4], &min_q1_size)) return enif_make_badarg(env); c = new_cache(atom, max_size, min_q1_size); enif_consume_timeslice(env, 20); } if (argc > 5) if (!enif_get_uint64(env, argv[5], &lifetime)) return enif_make_badarg(env); n = enif_alloc(sizeof(*n)); memset(n, 0, sizeof(*n)); n->c = c; n->vsize = vbin.size; n->ksize = kbin.size; n->size = vbin.size + kbin.size; n->key = enif_alloc(kbin.size); memcpy(n->key, kbin.data, kbin.size); n->val = enif_alloc_resource(value_type, vbin.size); memcpy(n->val, vbin.data, vbin.size); n->q = &(c->q1); if (lifetime) { clock_now(&(n->expiry)); n->expiry.tv_sec += lifetime; } enif_rwlock_rwlock(c->cache_lock); enif_rwlock_rwlock(c->lookup_lock); HASH_FIND(hh, c->lookup, kbin.data, kbin.size, ng); if (ng) { enif_mutex_lock(c->ctrl_lock); destroy_cache_node(ng); enif_mutex_unlock(c->ctrl_lock); } TAILQ_INSERT_HEAD(&(c->q1.head), n, entry); c->q1.size += n->size; HASH_ADD_KEYPTR(hh, c->lookup, n->key, n->ksize, n); if (lifetime) { struct cache_node *rn; rn = RB_INSERT(expiry_tree, &(c->expiry_head), n); /* it's possible to get two timestamps that are the same, if this happens just bump us forwards by 1 usec until we're unique */ while (rn != NULL) { ++(n->expiry.tv_nsec); rn = RB_INSERT(expiry_tree, &(c->expiry_head), n); } } enif_rwlock_rwunlock(c->lookup_lock); enif_rwlock_rwunlock(c->cache_lock); enif_cond_broadcast(c->check_cond); enif_consume_timeslice(env, 50); return enif_make_atom(env, "ok"); }