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
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
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<PgfConcr> transient_concr_revisions;
#ifdef _WIN32
/* Stores a Reader/Writer lock for Windows */
LONG lock;
#endif
};
PGF_INTERNAL
@@ -342,29 +362,62 @@ PgfDB::PgfDB(const char* filepath, int flags, int mode) {
this->filepath = strdup(filepath);
}
int code = 0;
#ifndef _WIN32
#ifndef MREMAP_MAYMOVE
if (fd >= 0) {
ms = (malloc_state*)
mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
code = errno;
} else {
ms = (malloc_state*) ::malloc(file_size);
code = ENOMEM;
}
#else
int mflags = (fd < 0) ? (MAP_PRIVATE | MAP_ANONYMOUS) : MAP_SHARED;
ms = (malloc_state*)
mmap(NULL, file_size, PROT_READ | PROT_WRITE, mflags, fd, 0);
code = errno;
#endif
if (ms == MAP_FAILED || ms == NULL) {
ms = NULL; // mark that ms is not created.
::free((void *) this->filepath);
int code = errno;
close(fd);
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
char *name;
char buf[256];
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),
NULL,
PAGE_READWRITE,
@@ -375,49 +428,41 @@ PgfDB::PgfDB(const char* filepath, int flags, int mode) {
FILE_MAP_WRITE,
0,0,file_size);
if (ms == NULL) {
code = last_error_to_errno();
CloseHandle(hMap);
hMap = INVALID_HANDLE_VALUE;
}
} else {
code = last_error_to_errno();
hMap = INVALID_HANDLE_VALUE;
ms = NULL;
}
} else {
code = ENOMEM;
name = NULL;
hMap = INVALID_HANDLE_VALUE;
ms = (malloc_state*) ::malloc(file_size);
}
if (ms == NULL) {
::free((void *) this->filepath);
int code = errno;
close(fd);
throw pgf_systemerror(code, filepath);
}
#endif
try {
rwlock = ipc_new_file_rwlock(this->filepath, &is_first);
} catch (pgf_systemerror e) {
#ifndef _WIN32
#ifndef MREMAP_MAYMOVE
if (fd < 0) {
::free(ms);
} else
#endif
munmap(ms,file_size);
#else
hRWEvent = CreateEvent(NULL, FALSE, FALSE, name);
if (hRWEvent == NULL) {
if (fd < 0) {
::free(ms);
} else {
UnmapViewOfFile(ms);
CloseHandle(hMap);
}
#endif
::free((void *) this->filepath);
close(fd);
throw e;
throw pgf_systemerror(code, filepath);
}
#endif
if (is_new) {
init_state(file_size);
@@ -437,6 +482,7 @@ PgfDB::PgfDB(const char* filepath, int flags, int mode) {
UnmapViewOfFile(ms);
CloseHandle(hMap);
}
CloseHandle(hRWEvent);
#endif
::free((void *) this->filepath);
@@ -493,13 +539,16 @@ PgfDB::~PgfDB() {
UnmapViewOfFile(ms);
CloseHandle(hMap);
}
CloseHandle(hRWEvent);
#endif
}
if (fd >= 0)
close(fd);
#ifndef _WIN32
ipc_release_file_rwlock(filepath, rwlock);
#endif
::free((void*) filepath);
}
@@ -550,6 +599,10 @@ void PgfDB::init_state(size_t size)
ms->revisions = 0;
ms->transient_revisions = 0;
ms->transient_concr_revisions = 0;
#if _WIN32
ms->lock = 0;
#endif
}
/* Take a chunk off a bin list. */
@@ -1005,30 +1058,61 @@ object PgfDB::malloc_internal(size_t bytes)
size_t new_size =
old_size + alloc_size;
if (fd >= 0) {
if (ftruncate(fd, new_size) < 0)
throw pgf_systemerror(errno, filepath);
}
malloc_state* new_ms;
#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
if (fd >= 0) {
if (munmap(ms, old_size) == -1)
throw pgf_systemerror(errno);
ms = NULL;
if (ftruncate(fd, new_size) < 0)
throw pgf_systemerror(errno, filepath);
new_ms =
(malloc_state*) mmap(0, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (new_ms == MAP_FAILED)
throw pgf_systemerror(errno);
} else {
new_ms = (malloc_state*) realloc(ms, new_size);
if (new_ms == NULL)
throw pgf_systemerror(ENOMEM);
}
#else
if (fd >= 0) {
if (ftruncate(fd, new_size) < 0)
throw pgf_systemerror(errno, filepath);
}
new_ms =
(malloc_state*) mremap(ms, old_size, new_size, MREMAP_MAYMOVE);
#endif
if (new_ms == MAP_FAILED)
throw pgf_systemerror(errno);
#endif
#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
ms = new_ms;
@@ -1246,16 +1330,142 @@ void PgfDB::sync()
if (res != 0)
throw pgf_systemerror(errno);
#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
}
DB_scope::DB_scope(PgfDB *db, DB_scope_mode m)
{
int res =
(m == READER_SCOPE) ? ipc_rwlock_rdlock(db->rwlock)
: ipc_rwlock_wrlock(db->rwlock);
if (res != 0)
throw pgf_systemerror(res);
db->lock(m);
save_db = current_db;
current_db = db;
@@ -1269,7 +1479,7 @@ DB_scope::~DB_scope()
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wterminate"
ipc_rwlock_unlock(current_db->rwlock);
current_db->unlock();
current_db = save_db;
current_base = current_db ? (unsigned char*) current_db->ms

View File

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

View File

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

View File

@@ -2,23 +2,12 @@
#define IPC_H
#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
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);
PGF_INTERNAL_DECL
void ipc_release_file_rwlock(const char* file_path,
ipc_rwlock_t *rwlock);
pthread_rwlock_t *rwlock);
#endif
#endif