#include <sys/types.h>
#include <sys/mman.h>
#include <sys/queue.h>
#include "namespace.h"
#include <stdlib.h>
#include "un-namespace.h"
#include "thr_private.h"
struct psh {
LIST_ENTRY(psh) link;
void *key;
void *val;
};
LIST_HEAD(pshared_hash_head, psh);
#define HASH_SIZE 128
static struct pshared_hash_head pshared_hash[HASH_SIZE];
#define PSHARED_KEY_HASH(key) (((unsigned long)(key) >> 8) % HASH_SIZE)
static struct urwlock pshared_lock = DEFAULT_URWLOCK;
static int page_size;
void
__thr_pshared_init(void)
{
int i;
page_size = getpagesize();
THR_ASSERT(page_size >= THR_PAGE_SIZE_MIN,
"THR_PAGE_SIZE_MIN is too large");
_thr_urwlock_init(&pshared_lock);
for (i = 0; i < HASH_SIZE; i++)
LIST_INIT(&pshared_hash[i]);
}
static void
pshared_rlock(struct pthread *curthread)
{
curthread->locklevel++;
_thr_rwl_rdlock(&pshared_lock);
}
static void
pshared_wlock(struct pthread *curthread)
{
curthread->locklevel++;
_thr_rwl_wrlock(&pshared_lock);
}
static void
pshared_unlock(struct pthread *curthread)
{
_thr_rwl_unlock(&pshared_lock);
curthread->locklevel--;
_thr_ast(curthread);
}
static void
pshared_gc(struct pthread *curthread)
{
struct pshared_hash_head *hd;
struct psh *h, *h1;
int error, i;
pshared_wlock(curthread);
for (i = 0; i < HASH_SIZE; i++) {
hd = &pshared_hash[i];
LIST_FOREACH_SAFE(h, hd, link, h1) {
error = _umtx_op(NULL, UMTX_OP_SHM, UMTX_SHM_ALIVE,
h->val, NULL);
if (error == 0)
continue;
LIST_REMOVE(h, link);
munmap(h->val, page_size);
free(h);
}
}
pshared_unlock(curthread);
}
static void *
pshared_lookup(void *key)
{
struct pshared_hash_head *hd;
struct psh *h;
hd = &pshared_hash[PSHARED_KEY_HASH(key)];
LIST_FOREACH(h, hd, link) {
if (h->key == key)
return (h->val);
}
return (NULL);
}
static int
pshared_insert(void *key, void **val)
{
struct pshared_hash_head *hd;
struct psh *h;
hd = &pshared_hash[PSHARED_KEY_HASH(key)];
LIST_FOREACH(h, hd, link) {
if (h->key == key) {
if (h->val != *val) {
munmap(*val, page_size);
*val = h->val;
}
return (1);
}
}
h = malloc(sizeof(*h));
if (h == NULL)
return (0);
h->key = key;
h->val = *val;
LIST_INSERT_HEAD(hd, h, link);
return (1);
}
static void *
pshared_remove(void *key)
{
struct pshared_hash_head *hd;
struct psh *h;
void *val;
hd = &pshared_hash[PSHARED_KEY_HASH(key)];
LIST_FOREACH(h, hd, link) {
if (h->key == key) {
LIST_REMOVE(h, link);
val = h->val;
free(h);
return (val);
}
}
return (NULL);
}
static void
pshared_clean(void *key, void *val)
{
if (val != NULL)
munmap(val, page_size);
_umtx_op(NULL, UMTX_OP_SHM, UMTX_SHM_DESTROY, key, NULL);
}
static void
pshared_destroy(struct pthread *curthread, void *key)
{
void *val;
pshared_wlock(curthread);
val = pshared_remove(key);
pshared_unlock(curthread);
pshared_clean(key, val);
}
void *
__thr_pshared_offpage(void *key, int doalloc)
{
struct pthread *curthread;
void *res;
int fd, ins_done;
curthread = _get_curthread();
if (doalloc) {
pshared_destroy(curthread, key);
res = NULL;
} else {
pshared_rlock(curthread);
res = pshared_lookup(key);
pshared_unlock(curthread);
if (res != NULL)
return (res);
}
fd = _umtx_op(NULL, UMTX_OP_SHM, doalloc ? UMTX_SHM_CREAT :
UMTX_SHM_LOOKUP, key, NULL);
if (fd == -1)
return (NULL);
res = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
if (res == MAP_FAILED)
return (NULL);
pshared_wlock(curthread);
ins_done = pshared_insert(key, &res);
pshared_unlock(curthread);
if (!ins_done) {
pshared_clean(key, res);
res = NULL;
}
return (res);
}
void
__thr_pshared_destroy(void *key)
{
struct pthread *curthread;
curthread = _get_curthread();
pshared_destroy(curthread, key);
pshared_gc(curthread);
}
void
__thr_pshared_atfork_pre(void)
{
_thr_rwl_rdlock(&pshared_lock);
}
void
__thr_pshared_atfork_post(void)
{
_thr_rwl_unlock(&pshared_lock);
}