[sheepdog] [PATCH v2 3/6] sheep/cluster: let local_lock() support nested
Liu Yuan
namei.unix at gmail.com
Tue Feb 25 07:19:29 CET 2014
On Tue, Feb 25, 2014 at 02:07:21PM +0800, Robin Dong wrote:
> From: Robin Dong <sanbai at taobao.com>
>
> In kv_create_object(), we need to lock bucket and then lock allocator of it:
>
> lock(bucket_vid)
> lock(data_vid)
> unlock(data_vid)
> unlock(bucket_vid)
>
> so the implementations of ->lock() for 'local' in cluster must support nested
> lock.
>
> The pthread_mutex_t with attribute of "PTHREAD_PROCESS_SHARED" could used by
> threads in different processes.
> (ref: http://stackoverflow.com/questions/17809231/mutex-attribute-pthread-process-shared-inverts-logic,
> http://royontechnology.blogspot.com/2007/06/comparison-of-pthreadprocessshared-in.html)
>
> First, we create a 'global_lock' to protect lock_tree, then we can create new
> 'SHARED' lock to use. Since the pthread_mutex_t is actually store in files, we
> must check the exists of files before init mutex on mmap-memory.
>
> Signed-off-by: Robin Dong <sanbai at taobao.com>
> ---
> sheep/cluster/local.c | 108 ++++++++++++++++++++++++++++++++++++++++++--------
> 1 file changed, 92 insertions(+), 16 deletions(-)
>
> diff --git a/sheep/cluster/local.c b/sheep/cluster/local.c
> index 7f9a6eb..ceaa091 100644
> --- a/sheep/cluster/local.c
> +++ b/sheep/cluster/local.c
> @@ -30,17 +30,24 @@
>
> static const char *shmfile = "/tmp/sheepdog_shm";
> static const char *lockdir = "/tmp/sheepdog_locks/";
> -/*
> - * we have to use sd_rw_lock because flock isn't thread exclusive
> - * and it also serves to project lock_tree
> - */
> -static struct sd_rw_lock lock_tree_lock = SD_RW_LOCK_INITIALIZER;
> +
> +/* use lock_tree to find lock quickly */
> static struct rb_root lock_tree_root = RB_ROOT;
>
> +/* use global_lock to protect lock_tree */
> +static pthread_mutex_t *global_lock;
> +
> +/*
> + * a lock may be used by several processes(or threads) at the same time,
> + * so we should add 'ref' to avoid one process release a lock which
> + * still used by another process.
> + */
> struct lock_entry {
> struct rb_node rb;
> int fd;
> uint64_t lock_id;
> + uint64_t ref;
> + pthread_mutex_t *mutex;
> };
>
> static int shmfd;
> @@ -539,10 +546,64 @@ static int local_get_local_addr(uint8_t *myaddr)
> return 0;
> }
>
> +/*
> + * pthread_mutex with attribute of PTHREAD_PROCESS_SHARED could be
> + * used by different threads in different processes.
> + * We put pthread_mutex_t in shared-memory so any process could easily
> + * get it.
> + */
> +static pthread_mutex_t *get_shared_lock(const char *path, int *fd)
> +{
> + pthread_mutex_t *pmutex;
> + pthread_mutexattr_t mutex_attr;
> + int ret, flags = O_RDWR;
> + bool created = false;
> +
> + ret = access(path, R_OK|W_OK);
> + if (!ret)
> + created = true;
> + else if (errno != ENOENT)
> + panic("failed to access %s, %m", path);
> +
> + if (!created)
> + flags |= O_CREAT;
> +
> + *fd = open(path, flags, sd_def_fmode);
> + if (*fd < 0)
> + panic("failed to open %s, %m", path);
> +
> + if (!created) {
> + ret = ftruncate(*fd, sizeof(pthread_mutex_t));
> + if (ret < 0)
> + panic("failed to ftruncate %s, %m", path);
> + }
> +
> + pmutex = (pthread_mutex_t *)mmap(NULL, sizeof(pthread_mutex_t),
> + PROT_READ|PROT_WRITE,
> + MAP_SHARED, *fd, 0);
> + if (!pmutex)
> + panic("failed to mmap %s, %m", path);
> +
> + if (!created) {
> + if (pthread_mutexattr_init(&mutex_attr))
> + panic("failed to init mutexattr, %m");
> +
> + if (pthread_mutexattr_setpshared(&mutex_attr,
> + PTHREAD_PROCESS_SHARED))
> + panic("failed to setpshared mutexattr, %m");
> +
> + if (pthread_mutex_init(pmutex, &mutex_attr))
> + panic("failed to init mutex, %m");
> + }
> +
> + return pmutex;
> +}
> +
> static int local_init(const char *option)
> {
> sigset_t mask;
> - int ret;
> + int ret, fd;
> + char path[PATH_MAX];
> static struct timer t = {
> .callback = check_pids,
> .data = &t,
> @@ -583,6 +644,9 @@ static int local_init(const char *option)
> return -1;
> }
>
> + snprintf(path, sizeof(path), "%s%s", lockdir, "global_lock");
> + global_lock = get_shared_lock(path, &fd);
> + sd_debug("create global_lock");
> return 0;
> }
>
> @@ -609,23 +673,27 @@ static void local_lock(uint64_t lock_id)
> {
> struct lock_entry *entry;
>
> - sd_write_lock(&lock_tree_lock);
> + pthread_mutex_lock(global_lock);
> +
> entry = lock_tree_lookup(lock_id);
> if (!entry) {
> char path[PATH_MAX];
> int fd;
>
> snprintf(path, sizeof(path), "%s%016"PRIx64, lockdir, lock_id);
> - fd = open(path, O_RDONLY | O_CREAT, sd_def_fmode);
> - if (fd < 0)
> - panic("failed to open %s, %m", path);
> entry = xmalloc(sizeof(*entry));
> entry->lock_id = lock_id;
> + entry->mutex = get_shared_lock(path, &fd);
> entry->fd = fd;
> + entry->ref = 0;
> lock_tree_add(entry);
> }
>
> - if (xflock(entry->fd, LOCK_EX) < 0)
> + entry->ref++;
> +
> + pthread_mutex_unlock(global_lock);
> +
> + if (pthread_mutex_lock(entry->mutex))
> panic("lock failed %"PRIx64", %m", lock_id);
> }
>
> @@ -633,17 +701,25 @@ static void local_unlock(uint64_t lock_id)
> {
> struct lock_entry *entry;
>
> + pthread_mutex_lock(global_lock);
> +
> entry = lock_tree_lookup(lock_id);
> if (!entry)
> panic("can't find fd for lock %"PRIx64, lock_id);
>
> - if (xflock(entry->fd, LOCK_UN) < 0)
> + if (pthread_mutex_unlock(entry->mutex))
> panic("unlock failed %"PRIx64", %m", lock_id);
>
> - close(entry->fd);
> - rb_erase(&entry->rb, &lock_tree_root);
> - free(entry);
> - sd_rw_unlock(&lock_tree_lock);
> + entry->ref--;
> +
> + if (!entry->ref) {
> + munmap(entry->mutex, sizeof(pthread_mutex_t));
> + close(entry->fd);
> + rb_erase(&entry->rb, &lock_tree_root);
> + free(entry);
> + }
> +
> + pthread_mutex_unlock(global_lock);
use sd_mutex_xxx helpers
Thanks
Yuan
More information about the sheepdog
mailing list