/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1985-2012 AT&T Intellectual Property * * and is licensed under the * * Eclipse Public License, Version 1.0 * * by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.eclipse.org/org/documents/epl-v10.html * * (with md5 checksum b35adb5213ca9657e911e9befb180842) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * Glenn Fowler * * David Korn * * Phong Vo * * * ***********************************************************************/ #pragma prototyped #include "asohdr.h" #if defined(_UWIN) && defined(_BLD_ast) || !_aso_semaphore NoN(aso_meth_semaphore) #else #include #include #include #define SPIN 1000000 typedef union Semun_u { int val; struct semid_ds* ds; unsigned short* array; } Semun_t; typedef struct APL_s { int id; size_t size; } APL_t; static void* aso_init_semaphore(void* data, const char* details) { APL_t* apl = (APL_t*)data; char* path; char* opt; size_t size; size_t n; int key; int id; int perm; struct sembuf sem; char tmp[64]; if (apl) { /* * semaphore 0 is the reference count * the id is dropped on last reference */ sem.sem_num = 0; sem.sem_op = -1; sem.sem_flg = IPC_NOWAIT; semop(apl->id, &sem, 1); sem.sem_op = 0; if (!semop(apl->id, &sem, 1)) semctl(apl->id, 0, IPC_RMID); free(apl); return 0; } perm = S_IRUSR|S_IWUSR; size = 128; if (path = (char*)details) while (opt = strchr(path, ',')) { if (strneq(path, "perm=", 5)) { if ((n = opt - (path + 5)) >= sizeof(tmp)) n = sizeof(tmp) - 1; memcpy(tmp, path + 5, n); tmp[n] = 0; perm = strperm(tmp, NiL, perm); } else if (strneq(path, "size=", 5)) { size = strtoul(path + 5, NiL, 0); if (size <= 1) return 0; } path = opt + 1; } key = (!path || !*path || streq(path, "private")) ? IPC_PRIVATE : (strsum(path, 0) & 0x7fff); for (;;) { if ((id = semget(key, size, IPC_CREAT|IPC_EXCL|perm)) >= 0) { /* * initialize all semaphores to 0 * this also sets the semaphore 0 ref count */ sem.sem_op = 1; sem.sem_flg = 0; for (sem.sem_num = 0; sem.sem_num < size; sem.sem_num++) if (semop(id, &sem, 1) < 0) { (void)semctl(id, 0, IPC_RMID); return 0; } break; } else if (errno == EINVAL && size > 3) size /= 2; else if (errno != EEXIST) return 0; else if ((id = semget(key, size, perm)) >= 0) { struct semid_ds ds; Semun_t arg; unsigned int k; /* * make sure all semaphores have been activated */ arg.ds = &ds; for (k = 0; k < SPIN; ASOLOOP(k)) { if (semctl(id, size-1, IPC_STAT, arg) < 0) return 0; if (ds.sem_otime) break; } if (k > SPIN) return 0; /* * bump the ref count */ sem.sem_num = 0; sem.sem_op = 1; sem.sem_flg = 0; if (semop(id, &sem, 1) < 0) return 0; break; } else if (errno == EINVAL && size > 3) size /= 2; else return 0; } if (!(apl = newof(0, APL_t, 1, 0))) return 0; apl->id = id; apl->size = size - 1; return apl; } static ssize_t aso_lock_semaphore(void* data, ssize_t k, void volatile* p) { APL_t* apl = (APL_t*)data; struct sembuf sem; if (!apl) return -1; if (k > 0) sem.sem_op = 1; else { sem.sem_op = -1; k = HASH(p, apl->size) + 1; } sem.sem_num = k; sem.sem_flg = 0; return semop(apl->id, &sem, 1) < 0 ? -1 : k; } Asometh_t _aso_meth_semaphore = { "semaphore", ASO_PROCESS|ASO_THREAD, aso_init_semaphore, aso_lock_semaphore }; #endif