#1
authoradam <anton@adamansky.com>
Tue, 30 Oct 2012 06:14:43 +0000 (13:14 +0700)
committeradam <anton@adamansky.com>
Tue, 30 Oct 2012 06:14:43 +0000 (13:14 +0700)
node/Makefile
node/binding.gyp
node/ejdb_args.h [new file with mode: 0644]
node/ejdb_cmd.h [new file with mode: 0644]
node/ejdb_logging.cc [new file with mode: 0644]
node/ejdb_logging.h [new file with mode: 0644]
node/ejdb_native.cc
node/ejdb_thread.h [new file with mode: 0644]
node/nbproject/configurations.xml
node/win32/pthread_mutex.h [new file with mode: 0644]

index e69de29..16a6411 100644 (file)
@@ -0,0 +1,9 @@
+
+
+all:
+       npm build .
+
+clean:
+       rm -rf ./build
+
+.PHONY:         all clean
\ No newline at end of file
index 75cc305..af7afc2 100644 (file)
@@ -2,7 +2,10 @@
   'targets': [
     {
       'target_name' : 'ejdb_native',
-      'sources' : ['ejdb_native.cc'],
+      'sources' : [
+            'ejdb_native.cc',
+            'ejdb_logging.cc'
+       ],
       'include_dirs': ['../tcejdb'],
       'libraries' : [
             '-L../../tcejdb',
             '-lbz2 -lz -lrt -lpthread -lm -lc'
       ],
       'cflags': [
+            '-g',
+            '-O0',
             '-fPIC',
+            '-D_GNU_SOURCE',
             '-D_FILE_OFFSET_BITS=64',
             '-D_LARGEFILE_SOURCE'
-       ]
+       ],
+       'cflags!': [ '-fno-exceptions' ],
+       'cflags_cc!': [ '-fno-exceptions' ]
     }
   ]
 }
\ No newline at end of file
diff --git a/node/ejdb_args.h b/node/ejdb_args.h
new file mode 100644 (file)
index 0000000..ff64add
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * File:   node_args.h
+ * Author: adam
+ *
+ * Created on October 30, 2012, 10:37 AM
+ */
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifndef EJDB_ARGS_H
+#define        EJDB_ARGS_H
+
+#include <v8.h>
+#include <node.h>
+
+
+#define REQ_ARGS(N)                                                     \
+  if (args.Length() < (N))                                              \
+    return ThrowException(Exception::TypeError(                         \
+                             String::New("Expected " #N " arguments")));
+
+#define REQ_STR_ARG(I, VAR)                                             \
+  if (args.Length() <= (I) || !args[I]->IsString())                     \
+    return ThrowException(Exception::TypeError(                         \
+                  String::New("Argument " #I " must be a string")));    \
+  String::Utf8Value VAR(args[I]->ToString());
+
+#define REQ_STRW_ARG(I, VAR)                                             \
+  if (args.Length() <= (I) || !args[I]->IsString())                     \
+    return ThrowException(Exception::TypeError(                         \
+                  String::New("Argument " #I " must be a string")));    \
+  String::Value VAR(args[I]->ToString());
+
+
+#define REQ_FUN_ARG(I, VAR)                                             \
+  if (args.Length() <= (I) || !args[I]->IsFunction())                   \
+    return ThrowException(Exception::TypeError(                         \
+                  String::New("Argument " #I " must be a function")));  \
+  Local<Function> VAR = Local<Function>::Cast(args[I]);
+
+#define REQ_OBJ_ARG(I, VAR)                                             \
+  if (args.Length() <= (I) || !args[I]->IsObject())                     \
+    return ThrowException(Exception::TypeError(                         \
+                  String::New("Argument " #I " must be a object")));    \
+  Local<Object> VAR = Local<Object>::Cast(args[I]);
+
+#define REQ_VAL_ARG(I, VAR)                                             \
+  if (args.Length() <= (I))                                                                                            \
+    return ThrowException(Exception::TypeError(                         \
+                  String::New("Argument " #I " must be a provided")));  \
+  Local<Value> VAR = args[I];
+
+
+#define REQ_ARR_ARG(I, VAR)                                             \
+  if (args.Length() <= (I) || !args[I]->IsArray())                      \
+    return ThrowException(Exception::TypeError(                         \
+                  String::New("Argument " #I " must be an array")));    \
+  Local<Array> VAR = Local<Array>::Cast(args[I]);
+
+
+#define REQ_EXT_ARG(I, VAR)                                             \
+  if (args.Length() <= (I) || !args[I]->IsExternal())                   \
+    return ThrowException(Exception::TypeError(                         \
+                              String::New("Argument " #I " invalid"))); \
+  Local<External> VAR = Local<External>::Cast(args[I]);
+
+#define OPT_INT_ARG(I, VAR, DEFAULT)                                    \
+  int VAR;                                                              \
+  if (args.Length() <= (I)) {                                           \
+    VAR = (DEFAULT);                                                    \
+  } else if (args[I]->IsInt32()) {                                      \
+    VAR = args[I]->Int32Value();                                        \
+  } else {                                                              \
+    return ThrowException(Exception::TypeError(                         \
+              String::New("Argument " #I " must be an integer")));      \
+  }
+
+#define REQ_INT32_ARG(I, VAR)                                             \
+  if (args.Length() <= (I) || !args[I]->IsInt32())                      \
+    return ThrowException(Exception::TypeError(                         \
+               String::New("Argument " #I " must be an integer")));     \
+  int32_t VAR = args[I]->Int32Value();
+
+
+#define REQ_INT64_ARG(I, VAR)                                             \
+  if (args.Length() <= (I) || !args[I]->IsNumber())                      \
+    return ThrowException(Exception::TypeError(                         \
+               String::New("Argument " #I " must be an number")));     \
+  int64_t VAR = args[I]->IntegerValue();
+
+
+
+#define REQ_NUM_ARG(I, VAR)                                             \
+  if (args.Length() <= (I) || !args[I]->IsNumber())                     \
+    return ThrowException(Exception::TypeError(                         \
+               String::New("Argument " #I " must be an number")));      \
+  double VAR = args[I]->ToNumber()->Value();
+
+
+#define REQ_BUFF_ARG(I, VAR)                                            \
+  if (!Buffer::HasInstance(args[I])) {                                  \
+     return ThrowException(Exception::Error(                            \
+                String::New("Argument " #I " must to be a buffer")));   \
+  }                                                                     \
+  Buffer* VAR = ObjectWrap::Unwrap<Buffer>(args[I]->ToObject());
+
+
+#endif /* EJDB_ARGS_H */
+
diff --git a/node/ejdb_cmd.h b/node/ejdb_cmd.h
new file mode 100644 (file)
index 0000000..f03ee8a
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * File:   ejdb_cmd.h
+ * Author: adam
+ *
+ * Created on October 30, 2012, 11:41 AM
+ */
+
+#ifndef EJDB_CMD_H
+#define        EJDB_CMD_H
+
+#include <uv.h>
+#include <v8.h>
+#include <string>
+
+
+namespace ejdb {
+
+    template < typename T, typename D = void > class EIOCmdTask {
+    public:
+
+        //uv request
+        uv_work_t uv_work;
+
+        v8::Persistent<v8::Function> cb;
+        T* wrapped;
+
+        //cmd spec
+        int cmd;
+        D* cmd_data;
+
+        //cmd return data
+        int cmd_ret;
+        int cmd_ret_data_length;
+        std::string cmd_ret_msg;
+
+        //entity type
+        int entity;
+
+        //Pointer to free_cmd_data function
+        void (*free_cmd_data)(EIOCmdTask<T, D>*);
+
+
+    public:
+
+        static void free_val(EIOCmdTask<T, D>* dtask) {
+            if (dtask->cmd_data) {
+                free(dtask->cmd_data);
+                dtask->cmd_data = NULL;
+            }
+        }
+
+        static void delete_val(EIOCmdTask<T, D>* dtask) {
+            if (dtask->cmd_data) {
+                delete dtask->cmd_data;
+                dtask->cmd_data = NULL;
+            }
+        }
+
+    public:
+
+        EIOCmdTask(const v8::Handle<v8::Function>& _cb, T* _wrapped, int _cmd,
+                D* _cmd_data, void (*_free_cmd_data)(EIOCmdTask<T, D>*)) :
+        wrapped(_wrapped), cmd(_cmd), cmd_data(_cmd_data), cmd_ret(0), cmd_ret_data_length(0), entity(0) {
+
+            this->free_cmd_data = _free_cmd_data;
+            this->cb = v8::Persistent<v8::Function>::New(_cb);
+            this->wrapped->Ref();
+            this->uv_work.data = this;
+        }
+
+        virtual ~EIOCmdTask() {
+            this->cb.Dispose();
+            this->wrapped->Unref();
+            if (this->free_cmd_data) {
+                this->free_cmd_data(this);
+            }
+        }
+    };
+}
+
+
+
+
+
+
+
+
+#endif /* EJDB_CMD_H */
+
diff --git a/node/ejdb_logging.cc b/node/ejdb_logging.cc
new file mode 100644 (file)
index 0000000..c4ba7ed
--- /dev/null
@@ -0,0 +1,145 @@
+#include "ejdb_logging.h"
+#include "ejdb_thread.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#ifdef _WIN32
+#include <io.h>
+static void __flockfile(FILE *stream) {
+    HANDLE hf;
+    int fd;
+    LARGE_INTEGER li;
+    fd = _fileno(stream);
+    hf = (HANDLE) _get_osfhandle(fd);
+    li.QuadPart = _filelengthi64(fd);
+    LockFile(hf, 0, 0, li.LowPart, li.HighPart);
+}
+
+static void __funlockfile(FILE *stream) {
+    HANDLE hf;
+    int fd;
+    LARGE_INTEGER li;
+    fd = _fileno(stream);
+    hf = (HANDLE) _get_osfhandle(fd);
+    li.QuadPart = _filelengthi64(fd);
+    UnlockFile(hf, 0, 0, li.LowPart, li.HighPart);
+}
+#endif
+#ifdef __unix
+
+static void __flockfile(FILE *stream) {
+    flockfile(stream);
+}
+
+static void __funlockfile(FILE *stream) {
+    funlockfile(stream);
+}
+#endif
+
+namespace ejdb {
+
+    EJMutex g_outputMtx;
+
+    const char* ej_errno_msg(int errno_rv) {
+        return strerror(errno_rv);
+    }
+
+    void ej_log_intern(int priority, const char* file, int line, const char* fmt, ...) {
+#ifndef _DEBUG
+        if (priority == LOG_DEBUG) {
+            return;
+        }
+#endif
+        //Timestamp
+        char tbuff[64];
+        time_t rawtime;
+        struct tm* timeinfo;
+        time(&rawtime);
+        timeinfo = localtime(&rawtime);
+        strftime(tbuff, 64, "%d %b %H:%M:%S -", timeinfo);
+
+        __flockfile(stderr);
+        __flockfile(stdout);
+        try {
+            switch (priority) {
+                case LOG_ERROR:
+                    if (file && line > 0) {
+                        fprintf(stderr, "%s ERROR %s:%d ", tbuff, file, line);
+                    } else {
+                        fprintf(stderr, "%s ERROR ", tbuff);
+                    }
+                    break;
+                case LOG_DEBUG:
+                    if (file && line > 0) {
+                        fprintf(stderr, "%s DEBUG %s:%d ", tbuff, file, line);
+                    } else {
+                        fprintf(stderr, "%s DEBUG ", tbuff);
+                    }
+                    break;
+                case LOG_INFO:
+                    if (file && line > 0) {
+                        fprintf(stderr, "%s INFO %s:%d ", tbuff, file, line);
+                    } else {
+                        fprintf(stderr, "%s INFO ", tbuff);
+                    }
+                    break;
+                case LOG_WARNING:
+                    if (file && line > 0) {
+                        fprintf(stderr, "%s WARN %s:%d ", tbuff, file, line);
+                    } else {
+                        fprintf(stderr, "%s WARN ", tbuff);
+                    }
+                    break;
+                default:
+                    if (file && line > 0) {
+                        fprintf(stderr, "%s %s:%d ", tbuff, file, line);
+                    } else {
+                        fprintf(stderr, "%s ", tbuff);
+                    }
+            }
+            va_list vl;
+            va_start(vl, fmt);
+            vfprintf(stderr, fmt, vl);
+            va_end(vl);
+            fprintf(stderr, "\n");
+        } catch (...) {
+            __funlockfile(stderr);
+            __funlockfile(stdout);
+            throw;
+        }
+        __funlockfile(stderr);
+        __funlockfile(stdout);
+    }
+
+    void ej_log_errno_status(const char* file, int line, int errno_rv, const std::string& msg) {
+        ej_log_errno_status(file, line, errno_rv, msg.c_str());
+    }
+
+    void ej_log_errno_status(const char* file, int line, int errno_rv, const char* msg) {
+        if (msg) {
+            ej_log_intern(LOG_WARNING, file, line, "%s, %s", msg, strerror(errno_rv));
+        } else {
+            ej_log_intern(LOG_WARNING, file, line, "%s", strerror(errno_rv));
+        }
+    }
+
+    void ej_log_ejerror(const char* file, int line, const EJError& err) {
+        ej_log_errno_status(file, line, err.errno_code, err.msg);
+    }
+
+    void EJError::errPrint() {
+        ej_log_intern(LOG_ERROR, this->location.c_str(), this->line, "%s, %s",
+                this->msg.c_str(),
+                strerror(this->errno_code));
+    }
+
+} //eof ejdb namespace
+
+
+
+
diff --git a/node/ejdb_logging.h b/node/ejdb_logging.h
new file mode 100644 (file)
index 0000000..296948c
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * File:   ejdb_logging.h
+ * Author: adam
+ *
+ * Created on October 30, 2012, 12:15 PM
+ */
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+
+#ifndef EJDB_LOGGING_H
+#define        EJDB_LOGGING_H
+
+#include <string>
+
+namespace ejdb {
+
+    class EJError;
+
+    //No throw exception
+#define EJ_NO_THROW throw()
+#define EJ_THROW(E) throw(E)
+#define EJ_THROW_ERR throw(ejdb::EJError)
+
+    //Logging
+#define LOG_ERROR 1
+#define LOG_WARNING 2
+#define LOG_INFO 3
+#define LOG_DEBUG 4
+
+
+#define EJ_LOG_EJERROR(err) ejdb::ej_log_mherror(__FILE__, __LINE__, err)
+#define EJ_ERRNO_THROW(errno_rv, msg) throw ejdb::EJError(msg, errno_rv, __FILE__, __LINE__)
+#define EJ_LOG_ERRNO_THROW(errno_rv, msg) ejdb::ej_log_errno_status(__FILE__, __LINE__, errno_rv, msg); throw ejdb::MHError(msg, errno_rv, __FILE__, __LINE__)
+#define EJ_LOG_ERRNO_STATUS(errno_rv, msg) ejdb::ej_log_errno_status(__FILE__, __LINE__, errno_rv, msg)
+
+#define EJ_LOG_ERROR(fmt,...) ej_log_intern(LOG_ERROR, __FILE__, __LINE__, fmt,##__VA_ARGS__)
+#define EJ_LOG_WARN(fmt,...) ej_log_intern(LOG_WARNING, __FILE__, __LINE__, fmt,##__VA_ARGS__)
+#define EJ_LOG_INFO(fmt,...) ej_log_intern(LOG_INFO, __FILE__, __LINE__, fmt,##__VA_ARGS__)
+
+#ifdef _DEBUG
+#define EJ_LOG_DBG(fmt, ...) ej_log_intern(LOG_DEBUG, __FILE__, __LINE__, fmt,##__VA_ARGS__)
+#else
+#define EJ_LOG_DBG(fmt, ...)
+#endif
+
+    void ej_log_intern(int priority, const char* file, int line, const char* fmt, ...);
+    void ej_log_ejerror(const char* file, int line, const EJError& err);
+    void ej_log_errno_status(const char* file, int line, int errno_rv, const std::string& msg);
+    void ej_log_errno_status(const char* file, int line, int errno_rv, const char* msg);
+
+
+#define EJ_ERRNO_MSG(err) ej_errno_msg(err)
+    const char* ej_errno_msg(int errno_rv);
+
+    /**
+     * Exception class
+     */
+    class EJError {
+    public:
+
+        //exception message
+        const std::string msg;
+        //error code
+        const int errno_code;
+
+        const std::string location;
+        const size_t line;
+
+    public:
+
+        virtual void errPrint();
+
+    public:
+
+        EJError(const EJError& src) : msg(src.msg), errno_code(src.errno_code), location(src.location), line(src.line) {
+        }
+
+        EJError(const char* _msg, int _errno_code = 0, const char* _location = "", size_t _line = 0) :
+        msg(_msg), errno_code(_errno_code), location(_location), line(_line) {
+        };
+
+        EJError(const std::string& _msg, int _errno_code = 0, const char* _location = "", size_t _line = 0) :
+        msg(_msg), errno_code(_errno_code), location(_location), line(_line) {
+        }
+
+        virtual ~EJError() {
+        }
+    };
+
+}
+
+
+
+#endif /* EJDB_LOGGING_H */
+
index 75bd8b8..7da86af 100644 (file)
@@ -3,19 +3,70 @@
 #include <node.h>
 #include <ejdb.h>
 #include <locale.h>
+#include <stdio.h>
+
+#include "ejdb_args.h"
+#include "ejdb_cmd.h"
+#include "ejdb_logging.h"
+#include "ejdb_thread.h"
 
 using namespace node;
 using namespace v8;
 
+
+#define DEFINE_INT64_CONSTANT(target, constant)                       \
+  (target)->Set(v8::String::NewSymbol(#constant),                         \
+                v8::Number::New((int64_t) constant),                      \
+                static_cast<v8::PropertyAttribute>(                       \
+                    v8::ReadOnly|v8::DontDelete))
+
 namespace ejdb {
 
-    void Init(v8::Handle<v8::Object> target) {
-        EJDB *jb = ejdbnew();
-        ejdbdel(jb);
+    class NodeEJDB : public ObjectWrap {
+        typedef EIOCmdTask<NodeEJDB> EJTask;
+
+        static Persistent<FunctionTemplate> constructor_template;
+
+        static Handle<Value> s_new_object(const Arguments& args) {
+            HandleScope scope;
+            REQ_STR_ARG(0, dbPath);
+
+            return scope.Close(args.This());
+        }
+
+        NodeEJDB() {
+        }
 
+        virtual ~NodeEJDB() {
+        }
+    public:
+
+        static void Init(Handle<Object> target) {
+            HandleScope scope;
+
+            Local<FunctionTemplate> t = FunctionTemplate::New(s_new_object);
+            constructor_template = Persistent<FunctionTemplate>::New(t);
+            constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
+            constructor_template->SetClassName(String::NewSymbol("NodeEJDB"));
+        }
+
+        void Ref() {
+            ObjectWrap::Ref();
+
+        }
+
+        void Unref() {
+            ObjectWrap::Unref();
+        }
+    };
+
+    Persistent<FunctionTemplate> NodeEJDB::constructor_template;
+
+    void Init(v8::Handle<v8::Object> target) {
 #ifdef __unix
         setlocale(LC_ALL, "en_US.UTF-8"); //todo review it
 #endif
+        ejdb::NodeEJDB::Init(target);
     }
 
 }
diff --git a/node/ejdb_thread.h b/node/ejdb_thread.h
new file mode 100644 (file)
index 0000000..f6557e6
--- /dev/null
@@ -0,0 +1,64 @@
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifndef EJDB_THREAD_H
+#define        EJDB_THREAD_H
+
+#ifdef _WIN32
+#include "win32/pthread_mutex.h"
+#else
+#include <pthread.h>
+#endif
+
+namespace ejdb {
+    /**
+     * Mutex
+     */
+    class EJMutex {
+        pthread_mutex_t     cs;
+        volatile bool       initialized;
+      public:
+        EJMutex() {
+            pthread_mutex_init(&cs, NULL);
+            initialized = true;
+        }
+        ~EJMutex() {
+            pthread_mutex_destroy(&cs);
+            initialized = false;
+        }
+        bool IsInitialized() {
+            return initialized;
+        }
+        void Lock() {
+            if (initialized) {
+                pthread_mutex_lock(&cs);
+            }
+        }
+        void Unlock() {
+            if (initialized) {
+                pthread_mutex_unlock(&cs);
+            }
+        }
+    };
+
+    /**
+     * Stack based mutex locking
+     */
+    class EJCriticalSection {
+      private:
+        EJMutex& mutex;
+      public:
+        EJCriticalSection(EJMutex& guard) : mutex(guard) {
+            mutex.Lock();
+        }
+        ~EJCriticalSection() {
+            mutex.Unlock();
+        }
+    };
+}
+
+
+
+#endif /* EJDB_THREAD_H */
+
index 2b05df8..3fe02ba 100644 (file)
@@ -9,9 +9,13 @@
             </df>
             <in>ejdb_native.node</in>
           </df>
+          <in>ejdb_native.node</in>
           <in>linker.lock</in>
         </df>
+        <in>Makefile</in>
+        <in>binding.Makefile</in>
         <in>config.gypi</in>
+        <in>ejdb_native.target.mk</in>
       </df>
       <df name="nodejs">
         <df name="benchmark">
diff --git a/node/win32/pthread_mutex.h b/node/win32/pthread_mutex.h
new file mode 100644 (file)
index 0000000..cb52d79
--- /dev/null
@@ -0,0 +1,241 @@
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifdef _WIN32
+
+#ifndef PTHREAD_MUTEX_H
+#define        PTHREAD_MUTEX_H
+
+/*
+ * Posix Threads library for Microsoft Windows
+ *
+ * Use at own risk, there is no implied warranty to this code.
+ * It uses undocumented features of Microsoft Windows that can change
+ * at any time in the future.
+ *
+ * (C) 2010 Lockless Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *  * Neither the name of Lockless Inc. nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include <winsock2.h>
+#include <errno.h>
+#include <sys/timeb.h>
+
+struct timespec {
+       long tv_sec; /* seconds */
+       long tv_nsec; /* nanoseconds */
+};
+
+static unsigned long long _pthread_time_in_ms(void)
+{
+       struct __timeb64 tb;
+
+       _ftime64(&tb);
+
+       return tb.time * 1000 + tb.millitm;
+}
+
+static unsigned long long _pthread_time_in_ms_from_timespec(const struct timespec *ts)
+{
+       unsigned long long t = ts->tv_sec * 1000;
+       t += ts->tv_nsec / 1000000;
+
+       return t;
+}
+
+static unsigned long long _pthread_rel_time_in_ms(const struct timespec *ts)
+{
+       unsigned long long t1 = _pthread_time_in_ms_from_timespec(ts);
+       unsigned long long t2 = _pthread_time_in_ms();
+
+       /* Prevent underflow */
+       if (t1 < t2) return 1;
+       return t1 - t2;
+}
+
+typedef CRITICAL_SECTION pthread_mutex_t;
+typedef unsigned int pthread_mutexattr_t;
+
+static int pthread_mutex_lock(pthread_mutex_t *m)
+{
+       EnterCriticalSection(m);
+       return 0;
+}
+
+static int pthread_mutex_unlock(pthread_mutex_t *m)
+{
+       LeaveCriticalSection(m);
+       return 0;
+}
+
+static int pthread_mutex_trylock(pthread_mutex_t *m)
+{
+       return TryEnterCriticalSection(m) ? 0 : EBUSY;
+}
+
+static int pthread_mutex_init(pthread_mutex_t *m, pthread_mutexattr_t *a)
+{
+       (void) a;
+       InitializeCriticalSection(m);
+
+       return 0;
+}
+
+static int pthread_mutex_destroy(pthread_mutex_t *m)
+{
+       DeleteCriticalSection(m);
+       return 0;
+}
+
+#define PTHREAD_MUTEX_INITIALIZER {(PRTL_CRITICAL_SECTION_DEBUG)-1,-1,0,0,0,0}
+#define PTHREAD_MUTEX_NORMAL 0
+#define PTHREAD_MUTEX_ERRORCHECK 1
+#define PTHREAD_MUTEX_RECURSIVE 2
+#define PTHREAD_MUTEX_DEFAULT 3
+#define PTHREAD_MUTEX_SHARED 4
+#define PTHREAD_MUTEX_PRIVATE 0
+
+#ifndef PTHREAD_PRIO_MULT
+# define PTHREAD_PRIO_MULT 32
+#endif
+
+static int pthread_mutexattr_init(pthread_mutexattr_t *a)
+{
+       *a = 0;
+       return 0;
+}
+
+static int pthread_mutexattr_destroy(pthread_mutexattr_t *a)
+{
+       (void) a;
+       return 0;
+}
+
+static int pthread_mutexattr_gettype(pthread_mutexattr_t *a, int *type)
+{
+       *type = *a & 3;
+
+       return 0;
+}
+
+static int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type)
+{
+       if ((unsigned) type > 3) return EINVAL;
+       *a &= ~3;
+       *a |= type;
+
+       return 0;
+}
+
+static int pthread_mutexattr_getpshared(pthread_mutexattr_t *a, int *type)
+{
+       *type = *a & 4;
+
+       return 0;
+}
+
+static int pthread_mutexattr_setpshared(pthread_mutexattr_t * a, int type)
+{
+       if ((type & 4) != type) return EINVAL;
+
+       *a &= ~4;
+       *a |= type;
+
+       return 0;
+}
+
+static int pthread_mutexattr_getprotocol(pthread_mutexattr_t *a, int *type)
+{
+       *type = *a & (8 + 16);
+
+       return 0;
+}
+
+static int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int type)
+{
+       if ((type & (8 + 16)) != 8 + 16) return EINVAL;
+
+       *a &= ~(8 + 16);
+       *a |= type;
+
+       return 0;
+}
+
+static int pthread_mutexattr_getprioceiling(pthread_mutexattr_t *a, int * prio)
+{
+       *prio = *a / PTHREAD_PRIO_MULT;
+       return 0;
+}
+
+static int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *a, int prio)
+{
+       *a &= (PTHREAD_PRIO_MULT - 1);
+       *a += prio * PTHREAD_PRIO_MULT;
+
+       return 0;
+}
+
+static int pthread_mutex_timedlock(pthread_mutex_t *m, const struct timespec *ts)
+{
+       unsigned long long t, ct;
+
+       struct _pthread_crit_t
+       {
+               void *debug;
+               LONG count;
+               LONG r_count;
+               HANDLE owner;
+               HANDLE sem;
+               ULONG_PTR spin;
+       };
+
+       /* Try to lock it without waiting */
+       if (!pthread_mutex_trylock(m)) return 0;
+
+       ct = _pthread_time_in_ms();
+       t = _pthread_time_in_ms_from_timespec(ts);
+
+       while (1)
+       {
+               /* Have we waited long enough? */
+               if (ct > t) return ETIMEDOUT;
+
+               /* Wait on semaphore within critical section */
+               WaitForSingleObject(((struct _pthread_crit_t *)m)->sem, (DWORD)(t - ct));
+
+               /* Try to grab lock */
+               if (!pthread_mutex_trylock(m)) return 0;
+
+               /* Get current time */
+               ct = _pthread_time_in_ms();
+       }
+}
+
+#endif /* PTHREAD_MUTEX_H */
+#endif  /* _WIN32 */