1
0
forked from GitHub/gf-core

finished porting to Windows

This commit is contained in:
krangelov
2021-11-13 05:40:38 +01:00
parent 397d22b49b
commit 92ecc8cc1d
4 changed files with 266 additions and 90 deletions

View File

@@ -22,6 +22,21 @@ size_t getpagesize()
} }
#define ftruncate _chsize #define ftruncate _chsize
static
int last_error_to_errno()
{
switch (GetLastError()) {
case ERROR_SUCCESS:
return 0;
case ERROR_OUTOFMEMORY:
return ENOMEM;
case ERROR_HANDLE_DISK_FULL:
return ENOSPC;
default:
return EINVAL;
}
}
#endif #endif
PGF_INTERNAL __thread unsigned char* current_base __attribute__((tls_model("initial-exec"))) = NULL; PGF_INTERNAL __thread unsigned char* current_base __attribute__((tls_model("initial-exec"))) = NULL;
@@ -300,6 +315,11 @@ 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
/* Stores a Reader/Writer lock for Windows */
LONG lock;
#endif
}; };
PGF_INTERNAL PGF_INTERNAL
@@ -342,29 +362,62 @@ PgfDB::PgfDB(const char* filepath, int flags, int mode) {
this->filepath = strdup(filepath); this->filepath = strdup(filepath);
} }
int code = 0;
#ifndef _WIN32 #ifndef _WIN32
#ifndef MREMAP_MAYMOVE #ifndef MREMAP_MAYMOVE
if (fd >= 0) { if (fd >= 0) {
ms = (malloc_state*) ms = (malloc_state*)
mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
code = errno;
} else { } else {
ms = (malloc_state*) ::malloc(file_size); ms = (malloc_state*) ::malloc(file_size);
code = ENOMEM;
} }
#else #else
int mflags = (fd < 0) ? (MAP_PRIVATE | MAP_ANONYMOUS) : MAP_SHARED; int mflags = (fd < 0) ? (MAP_PRIVATE | MAP_ANONYMOUS) : MAP_SHARED;
ms = (malloc_state*) ms = (malloc_state*)
mmap(NULL, file_size, PROT_READ | PROT_WRITE, mflags, fd, 0); mmap(NULL, file_size, PROT_READ | PROT_WRITE, mflags, fd, 0);
code = errno;
#endif #endif
if (ms == MAP_FAILED || ms == NULL) { if (ms == MAP_FAILED || ms == NULL) {
ms = NULL; // mark that ms is not created. ms = NULL; // mark that ms is not created.
::free((void *) this->filepath); ::free((void *) this->filepath);
int code = errno;
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 buf[256];
if (fd >= 0) { if (fd >= 0) {
BY_HANDLE_FILE_INFORMATION hInfo;
if (!GetFileInformationByHandle((HANDLE) _get_osfhandle(fd), &hInfo)) {
code = last_error_to_errno();
::free((void *) this->filepath);
close(fd);
throw pgf_systemerror(code);
}
sprintf(buf, "gf-rwevent-%lx-%lx-%lx",
hInfo.dwVolumeSerialNumber,
hInfo.nFileIndexHigh,
hInfo.nFileIndexLow);
name = buf;
hMap = CreateFileMapping((HANDLE) _get_osfhandle(fd), hMap = CreateFileMapping((HANDLE) _get_osfhandle(fd),
NULL, NULL,
PAGE_READWRITE, PAGE_READWRITE,
@@ -375,49 +428,41 @@ PgfDB::PgfDB(const char* filepath, int flags, int mode) {
FILE_MAP_WRITE, FILE_MAP_WRITE,
0,0,file_size); 0,0,file_size);
if (ms == NULL) { if (ms == NULL) {
code = last_error_to_errno();
CloseHandle(hMap); CloseHandle(hMap);
hMap = INVALID_HANDLE_VALUE; hMap = INVALID_HANDLE_VALUE;
} }
} else { } else {
code = last_error_to_errno();
hMap = INVALID_HANDLE_VALUE; hMap = INVALID_HANDLE_VALUE;
ms = NULL; ms = NULL;
} }
} else { } else {
code = ENOMEM;
name = NULL;
hMap = INVALID_HANDLE_VALUE; hMap = INVALID_HANDLE_VALUE;
ms = (malloc_state*) ::malloc(file_size); ms = (malloc_state*) ::malloc(file_size);
} }
if (ms == NULL) { if (ms == NULL) {
::free((void *) this->filepath); ::free((void *) this->filepath);
int code = errno;
close(fd); close(fd);
throw pgf_systemerror(code, filepath); throw pgf_systemerror(code, filepath);
} }
#endif
try { hRWEvent = CreateEvent(NULL, FALSE, FALSE, name);
rwlock = ipc_new_file_rwlock(this->filepath, &is_first); if (hRWEvent == NULL) {
} catch (pgf_systemerror e) {
#ifndef _WIN32
#ifndef MREMAP_MAYMOVE
if (fd < 0) {
::free(ms);
} else
#endif
munmap(ms,file_size);
#else
if (fd < 0) { if (fd < 0) {
::free(ms); ::free(ms);
} else { } else {
UnmapViewOfFile(ms); UnmapViewOfFile(ms);
CloseHandle(hMap); CloseHandle(hMap);
} }
#endif
::free((void *) this->filepath); ::free((void *) this->filepath);
close(fd); close(fd);
throw e; throw pgf_systemerror(code, filepath);
} }
#endif
if (is_new) { if (is_new) {
init_state(file_size); init_state(file_size);
@@ -437,6 +482,7 @@ PgfDB::PgfDB(const char* filepath, int flags, int mode) {
UnmapViewOfFile(ms); UnmapViewOfFile(ms);
CloseHandle(hMap); CloseHandle(hMap);
} }
CloseHandle(hRWEvent);
#endif #endif
::free((void *) this->filepath); ::free((void *) this->filepath);
@@ -493,13 +539,16 @@ PgfDB::~PgfDB() {
UnmapViewOfFile(ms); UnmapViewOfFile(ms);
CloseHandle(hMap); CloseHandle(hMap);
} }
CloseHandle(hRWEvent);
#endif #endif
} }
if (fd >= 0) if (fd >= 0)
close(fd); close(fd);
#ifndef _WIN32
ipc_release_file_rwlock(filepath, rwlock); ipc_release_file_rwlock(filepath, rwlock);
#endif
::free((void*) filepath); ::free((void*) filepath);
} }
@@ -550,6 +599,10 @@ void PgfDB::init_state(size_t size)
ms->revisions = 0; ms->revisions = 0;
ms->transient_revisions = 0; ms->transient_revisions = 0;
ms->transient_concr_revisions = 0; ms->transient_concr_revisions = 0;
#if _WIN32
ms->lock = 0;
#endif
} }
/* Take a chunk off a bin list. */ /* Take a chunk off a bin list. */
@@ -1005,30 +1058,61 @@ object PgfDB::malloc_internal(size_t bytes)
size_t new_size = size_t new_size =
old_size + alloc_size; old_size + alloc_size;
if (fd >= 0) {
if (ftruncate(fd, new_size) < 0)
throw pgf_systemerror(errno, filepath);
}
malloc_state* new_ms; malloc_state* new_ms;
#ifndef _WIN32 #ifndef _WIN32
// OSX mman and mman-win32 do not implement mremap or MREMAP_MAYMOVE // OSX do not implement mremap or MREMAP_MAYMOVE
#ifndef MREMAP_MAYMOVE #ifndef MREMAP_MAYMOVE
if (fd >= 0) { if (fd >= 0) {
if (munmap(ms, old_size) == -1) if (munmap(ms, old_size) == -1)
throw pgf_systemerror(errno); throw pgf_systemerror(errno);
ms = NULL;
if (ftruncate(fd, new_size) < 0)
throw pgf_systemerror(errno, filepath);
new_ms = new_ms =
(malloc_state*) mmap(0, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); (malloc_state*) mmap(0, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (new_ms == MAP_FAILED)
throw pgf_systemerror(errno);
} else { } else {
new_ms = (malloc_state*) realloc(ms, new_size); new_ms = (malloc_state*) realloc(ms, new_size);
if (new_ms == NULL)
throw pgf_systemerror(ENOMEM);
} }
#else #else
if (fd >= 0) {
if (ftruncate(fd, new_size) < 0)
throw pgf_systemerror(errno, filepath);
}
new_ms = new_ms =
(malloc_state*) mremap(ms, old_size, new_size, MREMAP_MAYMOVE); (malloc_state*) mremap(ms, old_size, new_size, MREMAP_MAYMOVE);
#endif
if (new_ms == MAP_FAILED) if (new_ms == MAP_FAILED)
throw pgf_systemerror(errno); throw pgf_systemerror(errno);
#endif
#else #else
if (fd >= 0) {
UnmapViewOfFile(ms);
CloseHandle(hMap);
ms = NULL;
hMap = CreateFileMapping((HANDLE) _get_osfhandle(fd),
NULL,
PAGE_READWRITE,
HIWORD(new_size), LOWORD(new_size),
NULL);
if (hMap == NULL) {
hMap = INVALID_HANDLE_VALUE;
throw pgf_systemerror(last_error_to_errno());
}
new_ms = (malloc_state*) MapViewOfFile(hMap,
FILE_MAP_WRITE,
0,0,new_size);
if (new_ms == NULL)
throw pgf_systemerror(last_error_to_errno());
} else {
new_ms = (malloc_state*) realloc(ms, new_size);
if (new_ms == NULL)
throw pgf_systemerror(ENOMEM);
}
#endif #endif
ms = new_ms; ms = new_ms;
@@ -1246,16 +1330,142 @@ void PgfDB::sync()
if (res != 0) if (res != 0)
throw pgf_systemerror(errno); throw pgf_systemerror(errno);
#else #else
if (current_db->fd > 0) {
if (!FlushViewOfFile(ms,size)) {
throw pgf_systemerror(last_error_to_errno());
}
}
#endif
}
#ifdef _WIN32
#define MAX_SPIN 50000
__forceinline __int16 ReaderCount(unsigned __int32 lock)
{
return lock & 0x00007FFF;
}
__forceinline __int32 SetReaders(unsigned __int32 lock, unsigned __int16 readers)
{
return (lock & ~0x00007FFF) | readers;
}
__forceinline __int16 WaitingCount(unsigned __int32 lock)
{
return (__int16) ((lock & 0x3FFF8000) >> 15);
}
__forceinline __int32 SetWaiting(unsigned __int32 lock, unsigned __int16 waiting)
{
return (lock & ~0x3FFF8000) | (waiting << 15);
}
__forceinline bool Writer(unsigned __int32 lock)
{
return (lock & 0x40000000) != 0;
}
__forceinline __int32 SetWriter(unsigned __int32 lock, bool writer)
{
if(writer)
return lock | 0x40000000;
else
return lock & ~0x40000000;
}
__forceinline bool AllClear(unsigned __int32 lock)
{
return (lock & 0x40007FFF) == 0;
}
#endif
void PgfDB::lock(DB_scope_mode m)
{
#ifndef _WIN32
int res =
(m == READER_SCOPE) ? pthread_rwlock_rdlock(rwlock)
: pthread_rwlock_wrlock(rwlock);
if (res != 0)
throw pgf_systemerror(res);
#else
for (int i = 0; ; ++i) {
unsigned __int32 temp = ms->lock;
if (m == READER_SCOPE && !Writer(temp)) {
if (InterlockedCompareExchange(&ms->lock, SetReaders(temp, ReaderCount(temp) + 1), temp) == temp)
return;
else
continue;
} else if (m == WRITER_SCOPE && AllClear(temp)) {
if (InterlockedCompareExchange(&ms->lock, SetWriter(temp, true), temp) == temp)
return;
else
continue;
} else {
if (i < MAX_SPIN) {
YieldProcessor();
continue;
}
//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)
continue;
i = 0; //Reset the spincount for the next time
WaitForSingleObject(hRWEvent, INFINITE);
do
{
temp = ms->lock;
} while (InterlockedCompareExchange(&ms->lock, SetWaiting(temp, WaitingCount(temp) - 1), temp) != temp);
}
}
#endif
}
void PgfDB::unlock()
{
#ifndef _WIN32
pthread_rwlock_unlock(rwlock);
#else
while (true) {
unsigned __int32 temp = ms->lock;
if (ReaderCount(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
//is cause a waiting to reader to wake, perform a spinlock, then go back to sleep
//We're the last reader and there's a pending write
//Wake one waiting writer
SetEvent(hRWEvent);
}
//Decrement reader count
if (InterlockedCompareExchange(&ms->lock, SetReaders(temp, ReaderCount(temp) - 1), temp) == temp)
break;
} else {
while(true) {
temp = ms->lock;
assert(Writer(temp));
if (WaitingCount(temp) == 0)
break;
//Note: this is thread-safe (there's guaranteed not to be another EndWrite simultaneously)
//Wake all waiting readers or writers, loop until wake confirmation is received
SetEvent(hRWEvent);
}
//Decrement writer count
if (InterlockedCompareExchange(&ms->lock, SetWriter(temp, false), temp) == temp)
break;
}
}
#endif #endif
} }
DB_scope::DB_scope(PgfDB *db, DB_scope_mode m) DB_scope::DB_scope(PgfDB *db, DB_scope_mode m)
{ {
int res = db->lock(m);
(m == READER_SCOPE) ? ipc_rwlock_rdlock(db->rwlock)
: ipc_rwlock_wrlock(db->rwlock);
if (res != 0)
throw pgf_systemerror(res);
save_db = current_db; save_db = current_db;
current_db = db; current_db = db;
@@ -1269,7 +1479,7 @@ DB_scope::~DB_scope()
{ {
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wterminate" #pragma GCC diagnostic ignored "-Wterminate"
ipc_rwlock_unlock(current_db->rwlock); current_db->unlock();
current_db = save_db; current_db = save_db;
current_base = current_db ? (unsigned char*) current_db->ms current_base = current_db ? (unsigned char*) current_db->ms

View File

@@ -54,7 +54,11 @@ public:
} }
}; };
#ifndef _WIN32
#include "ipc.h" #include "ipc.h"
#endif
enum DB_scope_mode {READER_SCOPE, WRITER_SCOPE};
class PgfDB { class PgfDB {
private: private:
@@ -62,10 +66,11 @@ private:
const char *filepath; const char *filepath;
malloc_state* ms; malloc_state* ms;
ipc_rwlock_t *rwlock; #ifndef _WIN32
pthread_rwlock_t *rwlock;
#ifdef _WIN32 #else
HANDLE hMap; HANDLE hMap;
HANDLE hRWEvent;
#endif #endif
friend class PgfReader; friend class PgfReader;
@@ -109,11 +114,12 @@ 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);
void lock(DB_scope_mode m);
void unlock();
friend class DB_scope; friend class DB_scope;
}; };
enum DB_scope_mode {READER_SCOPE, WRITER_SCOPE};
class PGF_INTERNAL_DECL DB_scope { class PGF_INTERNAL_DECL DB_scope {
public: public:
DB_scope(PgfDB *db, DB_scope_mode m); DB_scope(PgfDB *db, DB_scope_mode m);

View File

@@ -1,11 +1,13 @@
//#define DEBUG_IPC //#define DEBUG_IPC
#ifndef _WIN32
#ifdef DEBUG_IPC #ifdef DEBUG_IPC
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#define PGF_INTERNAL static #define PGF_INTERNAL static
void ipc_error() { void ipc_error() {
perror(NULL); perror(NULL);
exit(1); exit(1);
@@ -20,7 +22,6 @@ void ipc_toomany() {
#define ipc_toomany() throw pgf_error("Too many open grammars") #define ipc_toomany() throw pgf_error("Too many open grammars")
#endif #endif
#ifndef _WIN32
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <fcntl.h> #include <fcntl.h>
@@ -107,8 +108,8 @@ next:
} }
PGF_INTERNAL PGF_INTERNAL
ipc_rwlock_t *ipc_new_file_rwlock(const char* file_path, pthread_rwlock_t *ipc_new_file_rwlock(const char* file_path,
bool *is_first) bool *is_first)
{ {
if (file_path == NULL) { if (file_path == NULL) {
*is_first = true; *is_first = true;
@@ -258,7 +259,7 @@ ipc_rwlock_t *ipc_new_file_rwlock(const char* file_path,
PGF_INTERNAL PGF_INTERNAL
void ipc_release_file_rwlock(const char* file_path, void ipc_release_file_rwlock(const char* file_path,
ipc_rwlock_t *rwlock) pthread_rwlock_t *rwlock)
{ {
if (file_path == NULL) { if (file_path == NULL) {
pthread_rwlock_destroy(rwlock); pthread_rwlock_destroy(rwlock);
@@ -322,38 +323,6 @@ void ipc_release_file_rwlock(const char* file_path,
pthread_mutex_unlock(&locks->mutex); pthread_mutex_unlock(&locks->mutex);
} }
#else
PGF_INTERNAL
int ipc_rwlock_rdlock(ipc_rwlock_t *rwlock)
{
return 0;
}
PGF_INTERNAL
int ipc_rwlock_wrlock(ipc_rwlock_t *rwlock)
{
return 0;
}
PGF_INTERNAL
int ipc_rwlock_unlock(ipc_rwlock_t *rwlock)
{
return 0;
}
PGF_INTERNAL
ipc_rwlock_t *ipc_new_file_rwlock(const char* file_path,
bool *is_first)
{
return NULL;
}
PGF_INTERNAL
void ipc_release_file_rwlock(const char* file_path,
ipc_rwlock_t *rwlock)
{
}
#endif
#ifdef DEBUG_IPC #ifdef DEBUG_IPC
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@@ -364,12 +333,13 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
pthread_rwlock_t *rwlock = ipc_new_file_rwlock(argv[2]); bool is_first;
ipc_rwlock_t *rwlock = ipc_new_file_rwlock(argv[2], &is_first);
if (strcmp(argv[1],"r") == 0) { if (strcmp(argv[1],"r") == 0) {
pthread_rwlock_rdlock(rwlock); ipc_rwlock_rdlock(rwlock);
} else if (strcmp(argv[1],"w") == 0) { } else if (strcmp(argv[1],"w") == 0) {
pthread_rwlock_wrlock(rwlock); ipc_rwlock_wrlock(rwlock);
} }
fputs("> ", stdout); fputs("> ", stdout);
@@ -378,10 +348,11 @@ int main(int argc, char *argv[])
char buf[16]; char buf[16];
read(0, buf, sizeof(buf)); read(0, buf, sizeof(buf));
pthread_rwlock_unlock(rwlock); ipc_rwlock_unlock(rwlock);
ipc_release_file_rwlock(argv[2], rwlock); ipc_release_file_rwlock(argv[2], rwlock);
return 0; return 0;
} }
#endif #endif
#endif

View File

@@ -2,23 +2,12 @@
#define IPC_H #define IPC_H
#ifndef _WIN32 #ifndef _WIN32
#define ipc_rwlock_t pthread_rwlock_t
#define ipc_rwlock_rdlock pthread_rwlock_rdlock
#define ipc_rwlock_wrlock pthread_rwlock_wrlock
#define ipc_rwlock_unlock pthread_rwlock_unlock
#else
typedef struct ipc_rwlock_t ipc_rwlock_t;
int ipc_rwlock_rdlock(ipc_rwlock_t *rwlock);
int ipc_rwlock_wrlock(ipc_rwlock_t *rwlock);
int ipc_rwlock_unlock(ipc_rwlock_t *rwlock);
#endif
PGF_INTERNAL_DECL PGF_INTERNAL_DECL
ipc_rwlock_t *ipc_new_file_rwlock(const char* file_path, pthread_rwlock_t *ipc_new_file_rwlock(const char* file_path,
bool *is_first); bool *is_first);
PGF_INTERNAL_DECL PGF_INTERNAL_DECL
void ipc_release_file_rwlock(const char* file_path, void ipc_release_file_rwlock(const char* file_path,
ipc_rwlock_t *rwlock); pthread_rwlock_t *rwlock);
#endif
#endif #endif