📄 semaphore.c
字号:
/* This file is part of GNUnet. (C) 2001, 2002, 2003, 2004 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*//** * @file util/os/semaphore.c * @brief functions related to IPC synchronization * * TODO: implement non-blocking semaphore down! */#include "platform.h"#include "gnunet_util_os.h"#include "gnunet_util_string.h"#include "gnunet_util_error.h"#include "gnunet_util_threads.h"#include "gnunet_util_disk.h"#if SOLARIS || GNUNET_freeBSD || OSX#include <semaphore.h>#endif#if SOMEBSD# include <sys/file.h>#endif#if LINUX# include <sys/ipc.h># include <sys/sem.h>#endif#ifdef _MSC_VER#include <semaphore.h>#endif/** * Shall we use error-checking (slow) * mutexes (e.g. for debugging) */#define USE_CHECKING_MUTEX 1typedef struct GNUNET_IPC_Semaphore{ struct GNUNET_GE_Context *ectx;#if SOLARIS || GNUNET_freeBSD5 || OSX sem_t *internal;#elif WINDOWS HANDLE internal;#elif LINUX int internal; char *filename;#elif SOMEBSD int initialValue; int fd; struct GNUNET_Mutex *internalLock; char *filename;#elif _MSC_VER int internal; /* KLB_FIX */ char *filename;#else /* PORT-ME! */#endif} IPC_SEMAPHORE;#if LINUX/* IPC semaphore kludging for linux *//* Why don't we start at count 0 and increment when opening? */#define PROCCOUNT 10000/** * Implementation for a single semaphore actually uses three : * * 0 : actual semaphore value * 1 : process counter * 2 : lock *//* Various operations */static struct sembuf op_lock[2] = { {2, 0, 0}, /* wait for [2] (lock) to equal 0 */ {2, 1, SEM_UNDO} /* then increment [2] to 1 - this locks it */ /* UNDO to release the lock if processes exits *//* before explicitly unlocking */};static struct sembuf op_unlock[1] = { {2, -1, SEM_UNDO} /* decrement [2] (lock) back to 0 */};static struct sembuf op_endcreate[2] = { {1, -1, SEM_UNDO}, /* decrement [1] (proc counter) with undo on exit */ /* UNDO to adjust proc counter if process exits before explicitly calling sem_close() */ {2, -1, SEM_UNDO} /* then decrement [2] (lock) back to 0 */};static struct sembuf op_close[3] = { {2, 0, 0}, /* wait for [2] (lock) to equal 0 */ {2, 1, SEM_UNDO}, /* then increment [2] to 1 - this locks it */ {1, 1, SEM_UNDO} /* then increment [1] (proc counter) */};#endif#if SOMEBSDstatic voidFLOCK (int fd, int operation){ int ret; ret = -1; while (ret == -1) { ret = flock (fd, operation); if (ret == -1) { if (errno != EINTR) { GNUNET_GE_LOG_STRERROR (NULL, GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_ADMIN | GNUNET_GE_BULK, "flock"); return; } } } fsync (fd);}static intSEMA_LSEEK (int fd, off_t pos, int mode){ int ret; ret = LSEEK (fd, pos, mode); if (ret == -1) GNUNET_GE_LOG_STRERROR (NULL, GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_ADMIN | GNUNET_GE_BULK, "lseek"); return ret;}#endifstruct GNUNET_IPC_Semaphore *GNUNET_IPC_semaphore_create (struct GNUNET_GE_Context *ectx, const char *basename, const unsigned int initialValue){ /* Could older FreeBSD use this too since this code can shorten the IPC name */#if SOLARIS || OSX || GNUNET_freeBSD5 char *noslashBasename; int i; struct GNUNET_IPC_Semaphore *ret; ret = GNUNET_malloc (sizeof (struct GNUNET_IPC_Semaphore)); ret->ectx = ectx; noslashBasename = GNUNET_expand_file_name (ectx, basename); for (i = strlen (noslashBasename); i > 0; i--) if (noslashBasename[i] == '/') noslashBasename[i] = '.'; /* first character MUST be /, but Solaris forbids it afterwards */ noslashBasename[0] = '/'; ret->internal = sem_open (noslashBasename, O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, /* 660 */ initialValue); while ((ret->internal == (void *) SEM_FAILED) && (errno == ENAMETOOLONG)) { char *halfBasename; if (strlen (noslashBasename) < 4) break; /* definitely OS error... */ /* FIXME: this might cause unintended mapping to same names */ halfBasename = noslashBasename + strlen (noslashBasename) / 2; /* cut in half */ halfBasename[0] = '/'; ret->internal = sem_open (halfBasename, O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, /* 660 */ initialValue); } if (ret->internal == (void *) SEM_FAILED) GNUNET_GE_DIE_STRERROR_FILE (ectx, GNUNET_GE_FATAL | GNUNET_GE_USER | GNUNET_GE_DEVELOPER | GNUNET_GE_IMMEDIATE, "sem_open", noslashBasename); GNUNET_free (noslashBasename); return ret;#elif WINDOWS char *noslashBasename; int i; struct GNUNET_IPC_Semaphore *ret; SECURITY_ATTRIBUTES sec; DWORD dwErr; ret = GNUNET_malloc (sizeof (struct GNUNET_IPC_Semaphore)); ret->ectx = ectx; noslashBasename = GNUNET_expand_file_name (ectx, basename); for (i = strlen (noslashBasename); i > 0; i--) if (noslashBasename[i] == '\\') noslashBasename[i] = '.'; /* must not contain backslashes */ sec.nLength = sizeof (SECURITY_ATTRIBUTES); sec.bInheritHandle = TRUE; sec.lpSecurityDescriptor = NULL; ret->internal = CreateSemaphore (&sec, initialValue, LONG_MAX, noslashBasename); dwErr = GetLastError (); if (!ret->internal && dwErr == ERROR_ALREADY_EXISTS) { ret->internal = OpenSemaphore (SEMAPHORE_MODIFY_STATE, TRUE, noslashBasename); dwErr = GetLastError (); } if (!ret->internal) { GNUNET_GE_LOG (ectx, GNUNET_GE_FATAL | GNUNET_GE_USER | GNUNET_GE_DEVELOPER | GNUNET_GE_BULK, _("Can't create semaphore: %i"), dwErr); GNUNET_GE_DIE_STRERROR_FILE (ectx, GNUNET_GE_FATAL | GNUNET_GE_USER | GNUNET_GE_DEVELOPER | GNUNET_GE_BULK, "OpenSemaphore", noslashBasename); } GNUNET_free (noslashBasename); return ret;#elif LINUX union semun { int val; struct semid_ds *buf; ushort *array; } semctl_arg; struct GNUNET_IPC_Semaphore *ret; key_t key; FILE *fp; int pcount; char *ebasename; ret = GNUNET_malloc (sizeof (struct GNUNET_IPC_Semaphore)); ret->ectx = ectx; ebasename = GNUNET_expand_file_name (ectx, basename); GNUNET_disk_directory_create_for_file (ectx, ebasename); fp = FOPEN (ebasename, "a+"); if (NULL == fp) { GNUNET_GE_LOG_STRERROR_FILE (ectx, GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_BULK, "fopen", ebasename); GNUNET_free (ret); GNUNET_free (ebasename); return NULL; } fclose (fp); key = ftok (ebasename, 'g');again: ret->internal = semget (key, 3, IPC_CREAT | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (ret->internal == -1) GNUNET_GE_DIE_STRERROR (ectx, GNUNET_GE_FATAL | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE, "semget"); if (semop (ret->internal, &op_lock[0], 2) < 0) { if (errno == EINVAL) goto again; else GNUNET_GE_DIE_STRERROR (ectx, GNUNET_GE_FATAL | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE, "semop"); } /* get process count */ if ((pcount = semctl (ret->internal, 1, GETVAL, 0)) < 0) GNUNET_GE_DIE_STRERROR (ectx, GNUNET_GE_FATAL | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE, "semctl"); if (pcount == 0) { semctl_arg.val = initialValue; if (semctl (ret->internal, 0, SETVAL, semctl_arg) < 0) GNUNET_GE_DIE_STRERROR (ectx, GNUNET_GE_FATAL | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE, "semtcl"); semctl_arg.val = PROCCOUNT; if (semctl (ret->internal, 1, SETVAL, semctl_arg) < 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -