forked from GitHub/gf-core
the r/w lock is now in the database itself
This commit is contained in:
@@ -10,8 +10,6 @@ pgfinclude_HEADERS = \
|
|||||||
libpgf_la_SOURCES = \
|
libpgf_la_SOURCES = \
|
||||||
pgf/db.cxx \
|
pgf/db.cxx \
|
||||||
pgf/db.h \
|
pgf/db.h \
|
||||||
pgf/ipc.cxx \
|
|
||||||
pgf/ipc.h \
|
|
||||||
pgf/text.cxx \
|
pgf/text.cxx \
|
||||||
pgf/text.h \
|
pgf/text.h \
|
||||||
pgf/pgf.cxx \
|
pgf/pgf.cxx \
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ size_t getpagesize()
|
|||||||
|
|
||||||
#define ftruncate _chsize
|
#define ftruncate _chsize
|
||||||
|
|
||||||
|
typedef DWORD pid_t;
|
||||||
|
|
||||||
|
#define getpid GetCurrentProcessId
|
||||||
|
|
||||||
static
|
static
|
||||||
int last_error_to_errno()
|
int last_error_to_errno()
|
||||||
{
|
{
|
||||||
@@ -286,6 +290,11 @@ typedef struct mchunk mbin;
|
|||||||
|
|
||||||
static char slovo[5] = {'S','L','O','V','O'};
|
static char slovo[5] = {'S','L','O','V','O'};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
pid_t pid;
|
||||||
|
object next;
|
||||||
|
} process_entry;
|
||||||
|
|
||||||
struct PGF_INTERNAL_DECL malloc_state
|
struct PGF_INTERNAL_DECL malloc_state
|
||||||
{
|
{
|
||||||
/* Each .ngf file starts with 'SLOVO' as in:
|
/* Each .ngf file starts with 'SLOVO' as in:
|
||||||
@@ -316,10 +325,13 @@ struct PGF_INTERNAL_DECL malloc_state
|
|||||||
ref<PgfPGF> transient_revisions;
|
ref<PgfPGF> transient_revisions;
|
||||||
ref<PgfConcr> transient_concr_revisions;
|
ref<PgfConcr> transient_concr_revisions;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifndef _WIN32
|
||||||
|
pthread_rwlock_t rwlock;
|
||||||
|
#else
|
||||||
/* Stores a Reader/Writer lock for Windows */
|
/* Stores a Reader/Writer lock for Windows */
|
||||||
LONG lock;
|
LONG rwlock;
|
||||||
#endif
|
#endif
|
||||||
|
process_entry p;
|
||||||
};
|
};
|
||||||
|
|
||||||
PGF_INTERNAL
|
PGF_INTERNAL
|
||||||
@@ -385,21 +397,6 @@ PgfDB::PgfDB(const char* filepath, int flags, int mode) {
|
|||||||
close(fd);
|
close(fd);
|
||||||
throw pgf_systemerror(code, filepath);
|
throw pgf_systemerror(code, filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
rwlock = ipc_new_file_rwlock(this->filepath, &is_first);
|
|
||||||
} catch (pgf_systemerror e) {
|
|
||||||
#ifndef MREMAP_MAYMOVE
|
|
||||||
if (fd < 0) {
|
|
||||||
::free(ms);
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
munmap(ms,file_size);
|
|
||||||
|
|
||||||
::free((void *) this->filepath);
|
|
||||||
close(fd);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
char *name;
|
char *name;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
@@ -490,6 +487,8 @@ PgfDB::PgfDB(const char* filepath, int flags, int mode) {
|
|||||||
throw pgf_error("Invalid file content");
|
throw pgf_error("Invalid file content");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register_process(&is_first);
|
||||||
|
|
||||||
if (is_first) {
|
if (is_first) {
|
||||||
// We must make sure that left-over transient revisions are
|
// We must make sure that left-over transient revisions are
|
||||||
// released. This may happen if a client process was killed
|
// released. This may happen if a client process was killed
|
||||||
@@ -520,8 +519,11 @@ PgfDB::PgfDB(const char* filepath, int flags, int mode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PGF_INTERNAL
|
PGF_INTERNAL
|
||||||
PgfDB::~PgfDB() {
|
PgfDB::~PgfDB()
|
||||||
|
{
|
||||||
if (ms != NULL) {
|
if (ms != NULL) {
|
||||||
|
unregister_process();
|
||||||
|
|
||||||
size_t size =
|
size_t size =
|
||||||
ms->top + chunksize(ptr(ms,ms->top)) + sizeof(size_t);
|
ms->top + chunksize(ptr(ms,ms->top)) + sizeof(size_t);
|
||||||
|
|
||||||
@@ -546,13 +548,94 @@ PgfDB::~PgfDB() {
|
|||||||
if (fd >= 0)
|
if (fd >= 0)
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
ipc_release_file_rwlock(filepath, rwlock);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
::free((void*) filepath);
|
::free((void*) filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PGF_INTERNAL
|
||||||
|
void PgfDB::register_process(bool *is_first)
|
||||||
|
{
|
||||||
|
process_entry *pentry = &ms->p;
|
||||||
|
object *plast = NULL;
|
||||||
|
|
||||||
|
if (ms->p.pid != 0) {
|
||||||
|
while (pentry != (process_entry *) ptr(ms,0)) {
|
||||||
|
#ifndef _WIN32
|
||||||
|
char proc_file[32];
|
||||||
|
sprintf(proc_file, "/proc/%d", pentry->pid);
|
||||||
|
bool alive = (access(proc_file, F_OK) == 0);
|
||||||
|
#else
|
||||||
|
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,
|
||||||
|
FALSE,pentry->pid);
|
||||||
|
DWORD dwExitCode = STILL_ACTIVE;
|
||||||
|
if (hProcess != NULL)
|
||||||
|
GetExitCodeProcess(hProcess,&dwExitCode);
|
||||||
|
bool alive = (dwExitCode == STILL_ACTIVE);
|
||||||
|
CloseHandle(hProcess);
|
||||||
|
#endif
|
||||||
|
if (!alive) {
|
||||||
|
// if there are dead processes -> remove them
|
||||||
|
if (plast == NULL) {
|
||||||
|
if (ms->p.next == 0) {
|
||||||
|
ms->p.pid = 0;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
object next = pentry->next;
|
||||||
|
*pentry = *((process_entry *) ptr(ms,next));
|
||||||
|
free_internal(next);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*plast = pentry->next;
|
||||||
|
free_internal(ofs(ms,pentry));
|
||||||
|
pentry = (process_entry *) ptr(ms, *plast);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
plast = &pentry->next;
|
||||||
|
pentry = (process_entry *) ptr(ms, *plast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plast == NULL) {
|
||||||
|
*is_first = true;
|
||||||
|
} else {
|
||||||
|
*is_first = false;
|
||||||
|
*plast = malloc_internal(sizeof(process_entry));
|
||||||
|
pentry = (process_entry*) ptr(ms,*plast);
|
||||||
|
pentry->next = 0;
|
||||||
|
}
|
||||||
|
pentry->pid = getpid();
|
||||||
|
}
|
||||||
|
|
||||||
|
PGF_INTERNAL
|
||||||
|
void PgfDB::unregister_process()
|
||||||
|
{
|
||||||
|
pid_t pid = getpid();
|
||||||
|
process_entry *pentry = &ms->p;
|
||||||
|
object *plast = NULL;
|
||||||
|
|
||||||
|
while (pentry != (process_entry *) ptr(ms,0)) {
|
||||||
|
if (pentry->pid == pid) {
|
||||||
|
if (plast == NULL) {
|
||||||
|
if (ms->p.next == 0) {
|
||||||
|
ms->p.pid = 0;
|
||||||
|
} else {
|
||||||
|
object next = pentry->next;
|
||||||
|
*pentry = *((process_entry *) ptr(ms,next));
|
||||||
|
free_internal(next);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*plast = pentry->next;
|
||||||
|
free_internal(ofs(ms,pentry));
|
||||||
|
pentry = (process_entry *) ptr(ms, *plast);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
plast = &pentry->next;
|
||||||
|
pentry = (process_entry *) ptr(ms, *plast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PGF_INTERNAL
|
PGF_INTERNAL
|
||||||
ref<PgfPGF> PgfDB::get_revision(PgfText *name)
|
ref<PgfPGF> PgfDB::get_revision(PgfText *name)
|
||||||
{
|
{
|
||||||
@@ -600,9 +683,25 @@ void PgfDB::init_state(size_t size)
|
|||||||
ms->transient_revisions = 0;
|
ms->transient_revisions = 0;
|
||||||
ms->transient_concr_revisions = 0;
|
ms->transient_concr_revisions = 0;
|
||||||
|
|
||||||
#if _WIN32
|
#ifndef _WIN32
|
||||||
ms->lock = 0;
|
pthread_rwlockattr_t attr;
|
||||||
|
if (pthread_rwlockattr_init(&attr) != 0) {
|
||||||
|
throw pgf_systemerror(errno);
|
||||||
|
}
|
||||||
|
if (pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED) != 0) {
|
||||||
|
pthread_rwlockattr_destroy(&attr);
|
||||||
|
throw pgf_systemerror(errno);
|
||||||
|
}
|
||||||
|
if (pthread_rwlock_init(&ms->rwlock, &attr) != 0) {
|
||||||
|
pthread_rwlockattr_destroy(&attr);
|
||||||
|
throw pgf_systemerror(errno);
|
||||||
|
}
|
||||||
|
pthread_rwlockattr_destroy(&attr);
|
||||||
|
#else
|
||||||
|
ms->rwlock = 0;
|
||||||
#endif
|
#endif
|
||||||
|
ms->p.pid = getpid();
|
||||||
|
ms->p.next = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Take a chunk off a bin list. */
|
/* Take a chunk off a bin list. */
|
||||||
@@ -1384,20 +1483,20 @@ void PgfDB::lock(DB_scope_mode m)
|
|||||||
{
|
{
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
int res =
|
int res =
|
||||||
(m == READER_SCOPE) ? pthread_rwlock_rdlock(rwlock)
|
(m == READER_SCOPE) ? pthread_rwlock_rdlock(&ms->rwlock)
|
||||||
: pthread_rwlock_wrlock(rwlock);
|
: pthread_rwlock_wrlock(&ms->rwlock);
|
||||||
if (res != 0)
|
if (res != 0)
|
||||||
throw pgf_systemerror(res);
|
throw pgf_systemerror(res);
|
||||||
#else
|
#else
|
||||||
for (int i = 0; ; ++i) {
|
for (int i = 0; ; ++i) {
|
||||||
unsigned __int32 temp = ms->lock;
|
unsigned __int32 temp = ms->rwlock;
|
||||||
if (m == READER_SCOPE && !Writer(temp)) {
|
if (m == READER_SCOPE && !Writer(temp)) {
|
||||||
if (InterlockedCompareExchange(&ms->lock, SetReaders(temp, ReaderCount(temp) + 1), temp) == temp)
|
if (InterlockedCompareExchange(&ms->rwlock, SetReaders(temp, ReaderCount(temp) + 1), temp) == temp)
|
||||||
return;
|
return;
|
||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
} else if (m == WRITER_SCOPE && AllClear(temp)) {
|
} else if (m == WRITER_SCOPE && AllClear(temp)) {
|
||||||
if (InterlockedCompareExchange(&ms->lock, SetWriter(temp, true), temp) == temp)
|
if (InterlockedCompareExchange(&ms->rwlock, SetWriter(temp, true), temp) == temp)
|
||||||
return;
|
return;
|
||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
@@ -1408,7 +1507,7 @@ void PgfDB::lock(DB_scope_mode m)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//The pending write operation is taking too long, so we'll drop to the kernel and wait
|
//The pending write operation is taking too long, so we'll drop to the kernel and wait
|
||||||
if (InterlockedCompareExchange(&ms->lock, SetWaiting(temp, WaitingCount(temp) + 1), temp) != temp)
|
if (InterlockedCompareExchange(&ms->rwlock, SetWaiting(temp, WaitingCount(temp) + 1), temp) != temp)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
i = 0; //Reset the spincount for the next time
|
i = 0; //Reset the spincount for the next time
|
||||||
@@ -1416,8 +1515,8 @@ void PgfDB::lock(DB_scope_mode m)
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
temp = ms->lock;
|
temp = ms->rwlock;
|
||||||
} while (InterlockedCompareExchange(&ms->lock, SetWaiting(temp, WaitingCount(temp) - 1), temp) != temp);
|
} while (InterlockedCompareExchange(&ms->rwlock, SetWaiting(temp, WaitingCount(temp) - 1), temp) != temp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1426,10 +1525,10 @@ void PgfDB::lock(DB_scope_mode m)
|
|||||||
void PgfDB::unlock()
|
void PgfDB::unlock()
|
||||||
{
|
{
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
pthread_rwlock_unlock(rwlock);
|
pthread_rwlock_unlock(&ms->rwlock);
|
||||||
#else
|
#else
|
||||||
while (true) {
|
while (true) {
|
||||||
unsigned __int32 temp = ms->lock;
|
unsigned __int32 temp = ms->rwlock;
|
||||||
if (ReaderCount(temp) > 0) {
|
if (ReaderCount(temp) > 0) {
|
||||||
if (ReaderCount(temp) == 1 && WaitingCount(temp) != 0) {
|
if (ReaderCount(temp) == 1 && WaitingCount(temp) != 0) {
|
||||||
//Note: this isn't nor has to be thread-safe, as the worst a duplicate notification can do
|
//Note: this isn't nor has to be thread-safe, as the worst a duplicate notification can do
|
||||||
@@ -1441,11 +1540,11 @@ void PgfDB::unlock()
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Decrement reader count
|
//Decrement reader count
|
||||||
if (InterlockedCompareExchange(&ms->lock, SetReaders(temp, ReaderCount(temp) - 1), temp) == temp)
|
if (InterlockedCompareExchange(&ms->rwlock, SetReaders(temp, ReaderCount(temp) - 1), temp) == temp)
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
while(true) {
|
while(true) {
|
||||||
temp = ms->lock;
|
temp = ms->rwlock;
|
||||||
assert(Writer(temp));
|
assert(Writer(temp));
|
||||||
if (WaitingCount(temp) == 0)
|
if (WaitingCount(temp) == 0)
|
||||||
break;
|
break;
|
||||||
@@ -1456,7 +1555,7 @@ void PgfDB::unlock()
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Decrement writer count
|
//Decrement writer count
|
||||||
if (InterlockedCompareExchange(&ms->lock, SetWriter(temp, false), temp) == temp)
|
if (InterlockedCompareExchange(&ms->rwlock, SetWriter(temp, false), temp) == temp)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,10 +54,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
#include "ipc.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum DB_scope_mode {READER_SCOPE, WRITER_SCOPE};
|
enum DB_scope_mode {READER_SCOPE, WRITER_SCOPE};
|
||||||
|
|
||||||
class PgfDB {
|
class PgfDB {
|
||||||
@@ -66,9 +62,7 @@ private:
|
|||||||
const char *filepath;
|
const char *filepath;
|
||||||
malloc_state* ms;
|
malloc_state* ms;
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifdef _WIN32
|
||||||
pthread_rwlock_t *rwlock;
|
|
||||||
#else
|
|
||||||
HANDLE hMap;
|
HANDLE hMap;
|
||||||
HANDLE hRWEvent;
|
HANDLE hRWEvent;
|
||||||
#endif
|
#endif
|
||||||
@@ -114,6 +108,10 @@ private:
|
|||||||
PGF_INTERNAL_DECL object malloc_internal(size_t bytes);
|
PGF_INTERNAL_DECL object malloc_internal(size_t bytes);
|
||||||
PGF_INTERNAL_DECL void free_internal(object o);
|
PGF_INTERNAL_DECL void free_internal(object o);
|
||||||
|
|
||||||
|
PGF_INTERNAL_DECL void register_process(bool *is_first);
|
||||||
|
PGF_INTERNAL_DECL void unregister_process();
|
||||||
|
|
||||||
|
|
||||||
void lock(DB_scope_mode m);
|
void lock(DB_scope_mode m);
|
||||||
void unlock();
|
void unlock();
|
||||||
|
|
||||||
|
|||||||
@@ -1,358 +0,0 @@
|
|||||||
//#define DEBUG_IPC
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
#ifdef DEBUG_IPC
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#define PGF_INTERNAL static
|
|
||||||
|
|
||||||
void ipc_error() {
|
|
||||||
perror(NULL);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
void ipc_toomany() {
|
|
||||||
printf("Too many open grammars");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#include "pgf/data.h"
|
|
||||||
#define ipc_error() throw pgf_systemerror(errno);
|
|
||||||
#define ipc_toomany() throw pgf_error("Too many open grammars")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#define ptr_t(x) size_t
|
|
||||||
#define ptr(o,T) (o ? (T*) (((uint8_t*) locks) + o) : NULL)
|
|
||||||
#define offs(p) (((uint8_t*) p) - ((uint8_t*) locks))
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
pid_t pid;
|
|
||||||
ptr_t(process_entry) next;
|
|
||||||
} process_entry;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
dev_t dev;
|
|
||||||
ino_t ino;
|
|
||||||
process_entry p;
|
|
||||||
ptr_t(lock_entry) next;
|
|
||||||
pthread_rwlock_t rwlock;
|
|
||||||
} lock_entry;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
pthread_mutex_t mutex;
|
|
||||||
ptr_t(lock_entry) lock_entries;
|
|
||||||
ptr_t(lock_entry) free_lock_entries;
|
|
||||||
ptr_t(process_entry) free_process_entries;
|
|
||||||
size_t top;
|
|
||||||
} file_locks;
|
|
||||||
|
|
||||||
static char gf_runtime_locks[] = "/gf-runtime-locks";
|
|
||||||
|
|
||||||
static file_locks *locks = NULL;
|
|
||||||
|
|
||||||
static
|
|
||||||
void ipc_cleanup_dead_processes()
|
|
||||||
{
|
|
||||||
ptr_t(lock_entry) *last = &locks->lock_entries;
|
|
||||||
lock_entry *entry = ptr(*last, lock_entry);
|
|
||||||
while (entry != NULL) {
|
|
||||||
process_entry *pentry = &entry->p;
|
|
||||||
ptr_t(process_entry) *plast = NULL;
|
|
||||||
while (pentry != NULL) {
|
|
||||||
char proc_file[32];
|
|
||||||
sprintf(proc_file, "/proc/%d", pentry->pid);
|
|
||||||
if (access(proc_file, F_OK) != 0) {
|
|
||||||
// if there are dead processes -> remove them
|
|
||||||
if (plast == NULL) {
|
|
||||||
if (entry->p.next == 0) {
|
|
||||||
*last = entry->next;
|
|
||||||
entry->next = locks->free_lock_entries;
|
|
||||||
entry->dev = 0;
|
|
||||||
entry->ino = 0;
|
|
||||||
entry->p.pid = 0;
|
|
||||||
locks->free_lock_entries = offs(entry);
|
|
||||||
goto next;
|
|
||||||
} else {
|
|
||||||
process_entry *tmp =
|
|
||||||
ptr(pentry->next, process_entry);
|
|
||||||
*pentry = *tmp;
|
|
||||||
tmp->pid = 0;
|
|
||||||
tmp->next = locks->free_process_entries;
|
|
||||||
locks->free_process_entries = offs(tmp);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*plast = pentry->next;
|
|
||||||
pentry->pid = 0;
|
|
||||||
pentry->next = locks->free_process_entries;
|
|
||||||
locks->free_process_entries = offs(pentry);
|
|
||||||
pentry = ptr(*plast,process_entry);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
plast = &pentry->next;
|
|
||||||
pentry = ptr(*plast,process_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
last = &entry->next;
|
|
||||||
next:
|
|
||||||
entry = ptr(*last, lock_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PGF_INTERNAL
|
|
||||||
pthread_rwlock_t *ipc_new_file_rwlock(const char* file_path,
|
|
||||||
bool *is_first)
|
|
||||||
{
|
|
||||||
if (file_path == NULL) {
|
|
||||||
*is_first = true;
|
|
||||||
pthread_rwlock_t *rwlock = (pthread_rwlock_t *)
|
|
||||||
malloc(sizeof(pthread_rwlock_t));
|
|
||||||
if (pthread_rwlock_init(rwlock, NULL) != 0) {
|
|
||||||
ipc_error();
|
|
||||||
}
|
|
||||||
return rwlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
int pagesize = getpagesize();
|
|
||||||
|
|
||||||
if (locks == NULL) {
|
|
||||||
int created = 0;
|
|
||||||
|
|
||||||
// Uncomment if you want a clean state
|
|
||||||
//shm_unlink(gf_runtime_locks);
|
|
||||||
|
|
||||||
int fd =
|
|
||||||
shm_open(gf_runtime_locks, O_RDWR, 0);
|
|
||||||
if (errno == ENOENT) {
|
|
||||||
created = 1;
|
|
||||||
fd = shm_open(gf_runtime_locks, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
|
|
||||||
}
|
|
||||||
if (fd < 0) {
|
|
||||||
ipc_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ftruncate(fd, pagesize) != 0) {
|
|
||||||
close(fd);
|
|
||||||
ipc_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
locks =
|
|
||||||
(file_locks *)
|
|
||||||
mmap(NULL, pagesize,
|
|
||||||
PROT_READ|PROT_WRITE,
|
|
||||||
MAP_SHARED,
|
|
||||||
fd,0);
|
|
||||||
close(fd);
|
|
||||||
if (locks == MAP_FAILED) {
|
|
||||||
locks = NULL;
|
|
||||||
ipc_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (created) {
|
|
||||||
pthread_mutexattr_t attr;
|
|
||||||
if (pthread_mutexattr_init(&attr)) {
|
|
||||||
ipc_error();
|
|
||||||
}
|
|
||||||
if (pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED)) {
|
|
||||||
ipc_error();
|
|
||||||
}
|
|
||||||
if (pthread_mutex_init(&locks->mutex, &attr)) {
|
|
||||||
ipc_error();
|
|
||||||
}
|
|
||||||
pthread_mutexattr_destroy(&attr);
|
|
||||||
|
|
||||||
locks->lock_entries = 0;
|
|
||||||
locks->free_lock_entries = 0;
|
|
||||||
locks->free_process_entries = 0;
|
|
||||||
locks->top = sizeof(file_locks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct stat s;
|
|
||||||
if (stat(file_path, &s) != 0) {
|
|
||||||
ipc_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(&locks->mutex);
|
|
||||||
|
|
||||||
ipc_cleanup_dead_processes();
|
|
||||||
|
|
||||||
lock_entry *entry = ptr(locks->lock_entries, lock_entry);
|
|
||||||
while (entry != NULL) {
|
|
||||||
if (entry->dev == s.st_dev && entry->ino == s.st_ino) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
entry = ptr(entry->next, lock_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
*is_first = false;
|
|
||||||
|
|
||||||
if (entry == NULL) {
|
|
||||||
*is_first = true;
|
|
||||||
|
|
||||||
if (locks->free_lock_entries) {
|
|
||||||
entry = ptr(locks->free_lock_entries, lock_entry);
|
|
||||||
locks->free_lock_entries = entry->next;
|
|
||||||
} else {
|
|
||||||
if (locks->top + sizeof(lock_entry) > pagesize) {
|
|
||||||
pthread_mutex_unlock(&locks->mutex);
|
|
||||||
ipc_toomany();
|
|
||||||
}
|
|
||||||
entry = ptr(locks->top, lock_entry);
|
|
||||||
locks->top += sizeof(lock_entry);
|
|
||||||
|
|
||||||
pthread_rwlockattr_t attr;
|
|
||||||
if (pthread_rwlockattr_init(&attr) != 0) {
|
|
||||||
pthread_mutex_unlock(&locks->mutex);
|
|
||||||
ipc_error();
|
|
||||||
}
|
|
||||||
if (pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED) != 0) {
|
|
||||||
pthread_rwlockattr_destroy(&attr);
|
|
||||||
pthread_mutex_unlock(&locks->mutex);
|
|
||||||
ipc_error();
|
|
||||||
}
|
|
||||||
if (pthread_rwlock_init(&entry->rwlock, &attr) != 0) {
|
|
||||||
pthread_rwlockattr_destroy(&attr);
|
|
||||||
pthread_mutex_unlock(&locks->mutex);
|
|
||||||
ipc_error();
|
|
||||||
}
|
|
||||||
pthread_rwlockattr_destroy(&attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
entry->dev = s.st_dev;
|
|
||||||
entry->ino = s.st_ino;
|
|
||||||
entry->p.pid = getpid();
|
|
||||||
entry->p.next= 0;
|
|
||||||
entry->next = locks->lock_entries;
|
|
||||||
locks->lock_entries = offs(entry);
|
|
||||||
} else {
|
|
||||||
process_entry *pentry;
|
|
||||||
if (locks->free_process_entries) {
|
|
||||||
pentry = ptr(locks->free_process_entries,process_entry);
|
|
||||||
locks->free_process_entries = pentry->next;
|
|
||||||
} else {
|
|
||||||
if (locks->top+sizeof(process_entry) > pagesize) {
|
|
||||||
pthread_mutex_unlock(&locks->mutex);
|
|
||||||
ipc_toomany();
|
|
||||||
}
|
|
||||||
pentry = ptr(locks->top,process_entry);
|
|
||||||
locks->top += sizeof(process_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
pentry->pid = getpid();
|
|
||||||
pentry->next = entry->p.next;
|
|
||||||
entry->p.next = offs(pentry);
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&locks->mutex);
|
|
||||||
|
|
||||||
return &entry->rwlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
PGF_INTERNAL
|
|
||||||
void ipc_release_file_rwlock(const char* file_path,
|
|
||||||
pthread_rwlock_t *rwlock)
|
|
||||||
{
|
|
||||||
if (file_path == NULL) {
|
|
||||||
pthread_rwlock_destroy(rwlock);
|
|
||||||
free(rwlock);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (locks == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&locks->mutex);
|
|
||||||
|
|
||||||
ipc_cleanup_dead_processes();
|
|
||||||
|
|
||||||
lock_entry *entry = ptr(locks->lock_entries,lock_entry);
|
|
||||||
ptr_t(lock_entry) *last = &locks->lock_entries;
|
|
||||||
while (entry != NULL) {
|
|
||||||
if (&entry->rwlock == rwlock) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
last = &entry->next;
|
|
||||||
entry = ptr(*last,lock_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry != NULL) {
|
|
||||||
pid_t pid = getpid();
|
|
||||||
process_entry *pentry = &entry->p;
|
|
||||||
ptr_t(process_entry) *plast = NULL;
|
|
||||||
while (pentry != NULL) {
|
|
||||||
if (pentry->pid == pid) {
|
|
||||||
if (plast == NULL) {
|
|
||||||
if (entry->p.next == 0) {
|
|
||||||
*last = entry->next;
|
|
||||||
entry->next = locks->free_lock_entries;
|
|
||||||
entry->dev = 0;
|
|
||||||
entry->ino = 0;
|
|
||||||
entry->p.pid = 0;
|
|
||||||
locks->free_lock_entries = offs(entry);
|
|
||||||
} else {
|
|
||||||
process_entry *tmp =
|
|
||||||
ptr(pentry->next, process_entry);
|
|
||||||
*pentry = *tmp;
|
|
||||||
tmp->pid = 0;
|
|
||||||
tmp->next = locks->free_process_entries;
|
|
||||||
locks->free_process_entries = offs(tmp);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*plast = pentry->next;
|
|
||||||
pentry->pid = 0;
|
|
||||||
pentry->next = locks->free_process_entries;
|
|
||||||
locks->free_process_entries = offs(pentry);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
plast = &pentry->next;
|
|
||||||
pentry = ptr(*plast,process_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&locks->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_IPC
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
if (argc < 3 ||
|
|
||||||
(strcmp(argv[1], "r") != 0 && strcmp(argv[1], "w") != 0)) {
|
|
||||||
printf("syntax: %s (r|w) <file name>\n", argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_first;
|
|
||||||
ipc_rwlock_t *rwlock = ipc_new_file_rwlock(argv[2], &is_first);
|
|
||||||
|
|
||||||
if (strcmp(argv[1],"r") == 0) {
|
|
||||||
ipc_rwlock_rdlock(rwlock);
|
|
||||||
} else if (strcmp(argv[1],"w") == 0) {
|
|
||||||
ipc_rwlock_wrlock(rwlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
fputs("> ", stdout);
|
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
char buf[16];
|
|
||||||
read(0, buf, sizeof(buf));
|
|
||||||
|
|
||||||
ipc_rwlock_unlock(rwlock);
|
|
||||||
|
|
||||||
ipc_release_file_rwlock(argv[2], rwlock);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#ifndef IPC_H
|
|
||||||
#define IPC_H
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
PGF_INTERNAL_DECL
|
|
||||||
pthread_rwlock_t *ipc_new_file_rwlock(const char* file_path,
|
|
||||||
bool *is_first);
|
|
||||||
|
|
||||||
PGF_INTERNAL_DECL
|
|
||||||
void ipc_release_file_rwlock(const char* file_path,
|
|
||||||
pthread_rwlock_t *rwlock);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
Reference in New Issue
Block a user