github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/database/leveldb.chai2010/src/port/env_windows.cc (about)

     1  #include <stdio.h>
     2  #include <string.h>
     3  #include <deque>
     4  #include <process.h>
     5  
     6  #include "leveldb/env.h"
     7  #include "leveldb/slice.h"
     8  #include "port/port.h"
     9  #include "util/logging.h"
    10  #include "port/win_logger.h"
    11  
    12  // To properly support file names on Windows we should be using Unicode
    13  // (WCHAR) strings. To accomodate existing interface which uses std::string,
    14  // we use the following convention:
    15  // * all filenames that we return (e.g. from GetTestDirectory()) are
    16  //   utf8-encoded
    17  // * we'll try to interpret all input file names as if they're
    18  //   utf8-encoded. If they're not valid utf8 strings, we'll try
    19  //   to interpret them according to a current code page
    20  // This just works for names that don't use characters outside ascii
    21  // and for those that do, the caller needs to be aware of this convention
    22  // whenever it bubbles up to the user-level API.
    23  
    24  namespace leveldb {
    25  
    26  static Status IOError(const std::string& context, DWORD err = (DWORD)-1) {
    27    char *err_msg = NULL;
    28    Status s;
    29    if ((DWORD)-1 == err)
    30      err = GetLastError();
    31    FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    32        NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    33        (LPSTR)&err_msg, 0, NULL);
    34      if (!err_msg) 
    35          return Status::IOError(context);
    36      s = Status::IOError(context, err_msg);
    37      LocalFree(err_msg);
    38      return s;
    39  }
    40  
    41  class WinSequentialFile: public SequentialFile {
    42  private:
    43    std::string fname_;
    44    HANDLE file_;
    45  
    46  public:
    47    WinSequentialFile(const std::string& fname, HANDLE f)
    48        : fname_(fname), file_(f) { }
    49    virtual ~WinSequentialFile() { CloseHandle(file_); }
    50  
    51    virtual Status Read(size_t n, Slice* result, char* scratch) {
    52      DWORD n2 = n;
    53      DWORD r = 0;
    54      BOOL ok = ReadFile(file_, (void*)scratch, n2, &r, NULL);
    55      *result = Slice(scratch, r);
    56      if (!ok) {
    57          // We leave status as ok if we hit the end of the file
    58          if (GetLastError() != ERROR_HANDLE_EOF) {
    59              return IOError(fname_);
    60          }
    61      }
    62      return Status::OK();
    63    }
    64  
    65    virtual Status Skip(uint64_t n) {
    66      LARGE_INTEGER pos;
    67      pos.QuadPart = n;
    68      DWORD res = SetFilePointerEx(file_, pos, NULL, FILE_CURRENT);
    69      if (res == 0)
    70          return IOError(fname_);
    71      return Status::OK();
    72    }
    73  };
    74  
    75  class WinRandomAccessFile: public RandomAccessFile {
    76   private:
    77    std::string fname_;
    78    HANDLE file_;
    79  
    80   public:
    81    WinRandomAccessFile(const std::string& fname, HANDLE file)
    82        : fname_(fname), file_(file) { }
    83    virtual ~WinRandomAccessFile() { CloseHandle(file_); }
    84  
    85    virtual Status Read(uint64_t offset, size_t n, Slice* result,
    86                        char* scratch) const {
    87      OVERLAPPED overlapped = { 0 };
    88      overlapped.Offset = static_cast<DWORD>(offset);
    89      overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
    90      DWORD bytes_read = 0;
    91      BOOL success = ReadFile(file_, scratch, n, &bytes_read, &overlapped);
    92      *result = Slice(scratch, bytes_read);
    93      return success != FALSE ? Status::OK() : Status::IOError(fname_);
    94    }
    95  };
    96  
    97  class WinWritableFile : public WritableFile {
    98  private:
    99    std::string name_;
   100    HANDLE file_;
   101  
   102  public:
   103    WinWritableFile(std::string name, HANDLE h) : name_(name), file_(h) {
   104    }
   105  
   106    virtual ~WinWritableFile() {
   107      Close();
   108    }
   109  
   110    virtual Status Append(const Slice& data) {
   111      DWORD n = data.size();
   112      DWORD pos = 0;
   113      while (pos < n) {
   114        DWORD written = 0;
   115        BOOL ok = WriteFile(file_,  data.data() + pos, n - pos, &written, NULL);
   116        if (!ok)
   117          return IOError(name_+ "Append: cannot write");
   118        pos += written;
   119      }
   120      return Status::OK();
   121    }
   122  
   123    virtual Status Close() {
   124      if (INVALID_HANDLE_VALUE == file_)
   125        return Status::OK();
   126      Status s = Sync();
   127      CloseHandle(file_);
   128      file_ = INVALID_HANDLE_VALUE;
   129      return s;
   130    }
   131  
   132    virtual Status Flush() {
   133      return Status::OK();
   134    }
   135  
   136    virtual Status Sync() {
   137      BOOL ok = FlushFileBuffers(file_);
   138      if (!ok)
   139        return IOError(name_);
   140      return Status::OK();
   141    }
   142  };
   143  
   144  namespace {
   145  
   146  #define DIR_SEP_CHAR L'\\'
   147  #define DIR_SEP_STR L"\\"
   148  
   149  WCHAR *ToWcharFromCodePage(const char *src, UINT cp) {
   150    int required_buf_size = MultiByteToWideChar(cp, 0, src, -1, NULL, 0);
   151    if (0 == required_buf_size) // indicates an error
   152      return NULL;
   153    WCHAR *res = reinterpret_cast<WCHAR*>(malloc(sizeof(WCHAR) * required_buf_size));
   154    if (!res)
   155      return NULL;
   156    MultiByteToWideChar(cp, 0, src, -1, res, required_buf_size);
   157    return res;
   158  }
   159  
   160  // try to convert to WCHAR string trying most common code pages
   161  // to be as permissive as we can be
   162  WCHAR *ToWcharPermissive(const char *s) {
   163    WCHAR *ws = ToWcharFromCodePage(s, CP_UTF8);
   164    if (ws != NULL)
   165      return ws;
   166    ws = ToWcharFromCodePage(s, CP_ACP);
   167    if (ws != NULL)
   168      return ws;
   169    ws = ToWcharFromCodePage(s, CP_OEMCP);
   170    return ws;
   171  }
   172  
   173  char *ToUtf8(const WCHAR *s) {
   174      int required_buf_size = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
   175      char *res = (char*)malloc(sizeof(char) * required_buf_size);
   176      if (!res)
   177          return NULL;
   178      WideCharToMultiByte(CP_UTF8, 0, s, -1, res, required_buf_size, NULL, NULL);
   179      return res;
   180  }
   181  
   182  static size_t WstrLen(const WCHAR *s) {
   183      if (NULL == s)
   184          return 0;
   185      return wcslen(s);
   186  }
   187  
   188  static WCHAR *WstrJoin(const WCHAR *s1, const WCHAR *s2, const WCHAR *s3=NULL) {
   189      size_t s1_len = WstrLen(s1);
   190      size_t s2_len = WstrLen(s2);
   191      size_t s3_len = WstrLen(s3);
   192      size_t len =s1_len + s2_len + s3_len + 1;
   193      WCHAR *res = (WCHAR*)malloc(sizeof(WCHAR) * len);
   194      if (!res)
   195          return NULL;
   196      WCHAR *tmp = res;
   197      if (s1 != NULL) {
   198          memcpy(tmp, s1, s1_len * sizeof(WCHAR));
   199          tmp += s1_len;
   200      }
   201      if (s2 != NULL) {
   202          memcpy(tmp, s2, s2_len * sizeof(WCHAR));
   203          tmp += s2_len;
   204      }
   205      if (s3 != NULL) {
   206          memcpy(tmp, s3, s3_len * sizeof(WCHAR));
   207          tmp += s3_len;
   208      }
   209      *tmp = 0;
   210      return res;
   211  }
   212  
   213  static bool WstrEndsWith(const WCHAR *s1, WCHAR c) {
   214      size_t len = WstrLen(s1);
   215      return ((len > 0) && (s1[len-1] == c));
   216  }
   217  
   218  static WCHAR *WstrPathJoin(const WCHAR *s1, const WCHAR *s2) {
   219      if (WstrEndsWith(s1, DIR_SEP_CHAR))
   220          return WstrJoin(s1, s2);
   221      return WstrJoin(s1, DIR_SEP_STR, s2);
   222  }
   223  
   224  // Return true if s is "." or "..", which are 2 directories
   225  // we should skip when enumerating a directory
   226  static bool SkipDir(const WCHAR *s) {
   227      if (*s == L'.') {
   228        if (s[1] == 0)
   229          return true;
   230        return ((s[1] == '.') && (s[2] == 0));
   231      }
   232      return false;
   233  }
   234  
   235  class WinFileLock : public FileLock {
   236   public:
   237    WinFileLock(const std::string &fname, HANDLE file)
   238      : fname_(fname), file_(file) {
   239    }
   240  
   241    virtual ~WinFileLock() {
   242      Close();
   243    }
   244  
   245    bool Close() {
   246      bool ok = true;
   247      if (file_ != INVALID_HANDLE_VALUE)
   248        ok = (CloseHandle(file_) != FALSE);
   249      file_ = INVALID_HANDLE_VALUE;
   250      return ok;
   251    }
   252  
   253    std::string fname_;
   254    HANDLE file_;
   255  };
   256  
   257  class WinEnv : public Env {
   258   public:
   259    WinEnv();
   260    virtual ~WinEnv() {
   261      fprintf(stderr, "Destroying Env::Default()\n");
   262      //exit(1);
   263    }
   264  
   265    virtual Status NewSequentialFile(const std::string& fname,
   266                                     SequentialFile** result) {
   267      *result = NULL;
   268      WCHAR *file_name = ToWcharPermissive(fname.c_str());
   269      if (file_name == NULL) {
   270        return Status::InvalidArgument("Invalid file name");
   271      }
   272      HANDLE h = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
   273      free((void*)file_name);
   274      if (h == INVALID_HANDLE_VALUE) {
   275        return IOError(fname);
   276      }
   277      *result = new WinSequentialFile(fname, h);
   278      return Status::OK();
   279    }
   280  
   281    virtual Status NewRandomAccessFile(const std::string& fname,
   282                     RandomAccessFile** result) {
   283      *result = NULL;
   284      WCHAR *file_name = ToWcharPermissive(fname.c_str());
   285      if (file_name == NULL) {
   286        return Status::InvalidArgument("Invalid file name");
   287      }
   288      HANDLE h = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
   289      free((void*)file_name);
   290      if (h == INVALID_HANDLE_VALUE) {
   291        return IOError(fname);
   292      }
   293      *result = new WinRandomAccessFile(fname, h);
   294      return Status::OK();
   295    }
   296  
   297    virtual Status NewWritableFile(const std::string& fname,
   298                   WritableFile** result) {
   299      *result = NULL;
   300      WCHAR *file_name = ToWcharPermissive(fname.c_str());
   301      if (file_name == NULL)
   302        return Status::InvalidArgument("Invalid file name");
   303      HANDLE h = CreateFileW(file_name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
   304      free((void*)file_name);
   305      if (h == INVALID_HANDLE_VALUE) {
   306        return IOError(fname);
   307      }
   308      *result = new WinWritableFile(fname, h);
   309      return Status::OK();
   310    }
   311  
   312    virtual bool FileExists(const std::string& fname) {
   313      WCHAR *file_name = ToWcharPermissive(fname.c_str());
   314      if (file_name == NULL)
   315          return false;
   316  
   317      WIN32_FILE_ATTRIBUTE_DATA   file_info;
   318      BOOL res = GetFileAttributesExW(file_name, GetFileExInfoStandard, &file_info);
   319      free((void*)file_name);
   320      if (0 == res)
   321          return false;
   322  
   323      if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
   324          return false;
   325      return true;
   326    }
   327  
   328    virtual Status GetChildren(const std::string& dir,
   329                 std::vector<std::string>* result) {
   330      result->clear();
   331      WCHAR *dir_name = ToWcharPermissive(dir.c_str());
   332      if (dir_name == NULL)
   333        return Status::InvalidArgument("Invalid file name");
   334      WCHAR *pattern = WstrPathJoin(dir_name, L"*");
   335      free(dir_name);
   336      if (NULL == pattern)
   337        return Status::InvalidArgument("Invalid file name");
   338      WIN32_FIND_DATAW file_data;
   339      HANDLE h = FindFirstFileW(pattern, &file_data);
   340      free(pattern);
   341      if (INVALID_HANDLE_VALUE == h) {
   342          if (ERROR_FILE_NOT_FOUND == GetLastError())
   343            return Status::OK();
   344          return IOError(dir);
   345      }
   346      for (;;) {
   347          WCHAR *s = file_data.cFileName;
   348          if (!SkipDir(s)) {
   349              char *s2 = ToUtf8(s);
   350              result->push_back(s2);
   351              free(s2);
   352          }
   353          if (FALSE == FindNextFileW(h, &file_data))
   354              break;
   355      }
   356      FindClose(h);
   357      return Status::OK();
   358    }
   359  
   360    virtual Status DeleteFile(const std::string& fname) {
   361      WCHAR *file_path = ToWcharPermissive(fname.c_str());
   362      if (file_path == NULL)
   363          return Status::InvalidArgument("Invalid file name");
   364  
   365      BOOL ok = DeleteFileW(file_path);
   366      free(file_path);
   367      if (!ok) {
   368          DWORD err = GetLastError();
   369          if ((ERROR_PATH_NOT_FOUND == err) || (ERROR_FILE_NOT_FOUND == err))
   370            return Status::OK();
   371        return IOError("DeleteFile " + fname);
   372      }
   373      return Status::OK();
   374    }
   375  
   376    bool CreateDirIfNotExists(const WCHAR *dir) {
   377      BOOL ok = CreateDirectoryW(dir, NULL);
   378      if (ok)
   379        return true;
   380      return (ERROR_ALREADY_EXISTS == GetLastError());
   381    }
   382  
   383    bool DirExists(const WCHAR *dir) {
   384      WIN32_FILE_ATTRIBUTE_DATA   file_info;
   385      BOOL res = GetFileAttributesExW(dir, GetFileExInfoStandard, &file_info);
   386      if (0 == res)
   387          return false;
   388  
   389      return (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
   390    }
   391  
   392    WCHAR *WstrDupN(const WCHAR *s, size_t len) {
   393        void *res = malloc((len + 1) * sizeof(WCHAR));
   394        if (!res)
   395            return NULL;
   396        memcpy(res, s, len * sizeof(WCHAR));
   397        WCHAR *res2 = reinterpret_cast<WCHAR*>(res);
   398        res2[len] = 0;
   399        return res2;
   400    }
   401  
   402    bool IsPathSep(WCHAR c) {
   403        return (c == '\\') || (c == '/');
   404    }
   405  
   406    WCHAR *GetPathParent(const WCHAR *path) {
   407        const WCHAR *last_sep = NULL;
   408        const WCHAR *tmp = path;
   409        // find the last path separator 
   410        // (ignoring one at the end of the string)
   411        while (*tmp) {
   412            if (IsPathSep(*tmp)) {
   413                if (0 != tmp[1])
   414                    last_sep = tmp;
   415            }
   416            ++tmp;
   417        }
   418        if (NULL == last_sep)
   419            return NULL;
   420        size_t len = last_sep - path;
   421        return WstrDupN(path, len);
   422    }
   423  
   424    bool CreateDirRecursive(WCHAR *dir) {
   425      WCHAR *parent = GetPathParent(dir);
   426      bool ok = true;
   427      if (parent && !DirExists(parent)) {
   428          ok = CreateDirRecursive(parent);
   429      }
   430      free(parent);
   431      if (!ok)
   432          return false;
   433      return CreateDirIfNotExists(dir);
   434    }
   435  
   436    virtual Status CreateDir(const std::string& name) {
   437      WCHAR *dir = ToWcharPermissive(name.c_str());
   438      if (dir == NULL)
   439        return Status::InvalidArgument("Invalid file name");
   440      bool ok = CreateDirRecursive(dir);
   441      free(dir);
   442      if (!ok)
   443          return IOError(name);
   444      return Status::OK();
   445    }
   446  
   447  #if 1
   448    virtual Status DeleteDir(const std::string& name) {
   449      WCHAR *dir = ToWcharPermissive(name.c_str());
   450      if (dir == NULL)
   451        return Status::InvalidArgument("Invalid file name");
   452      BOOL ok = RemoveDirectoryW(dir);
   453      free(dir);
   454      if (!ok)
   455          return IOError(name);
   456      return Status::OK();
   457    }
   458  #else
   459    virtual Status DeleteDir(const std::string& dirname) {
   460      WCHAR *dir = ToWcharPermissive(dirname.c_str());
   461      if (dir == NULL)
   462        return Status::InvalidArgument("Invalid file name");
   463  
   464      SHFILEOPSTRUCTW fileop = { 0 };
   465      fileop.wFunc = FO_DELETE;
   466      fileop.pFrom = (const WCHAR*)dir;
   467      fileop.fFlags = FOF_NO_UI;
   468      int res = SHFileOperationW(&fileop);
   469      free((void*)dir);
   470      if (res == 0 && fileop.fAnyOperationsAborted == FALSE)
   471        return Status::OK();
   472      return IOError("DeleteDir " + dirname);
   473    }
   474  #endif
   475  
   476    virtual Status GetFileSize(const std::string& fname, uint64_t* size) {
   477  
   478      WCHAR *file_name = ToWcharPermissive(fname.c_str());
   479      if (file_name == NULL)
   480        return Status::InvalidArgument("Invalid file name");
   481      HANDLE h = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,  
   482                            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,  NULL); 
   483      free(file_name);
   484      if (h == INVALID_HANDLE_VALUE)
   485        return  IOError("GetFileSize " + fname);
   486  
   487      // Not using GetFileAttributesEx() as it doesn't interact well with symlinks, etc.
   488      LARGE_INTEGER lsize;
   489      BOOL ok = GetFileSizeEx(h, &lsize);
   490      CloseHandle(h);
   491      if (!ok)
   492        return  IOError("GetFileSize " + fname);
   493  
   494      *size = static_cast<uint64_t>(lsize.QuadPart);
   495      return Status::OK();
   496    }
   497  
   498    virtual Status RenameFile(const std::string& src, const std::string& target) {
   499      WCHAR *src2 = ToWcharPermissive(src.c_str());
   500      WCHAR *target2 = ToWcharPermissive(target.c_str());
   501      if ((src2 == NULL) || (target2 == NULL)) {
   502        free(src2);
   503        free(target2);
   504        return Status::InvalidArgument("Invalid file name");
   505      }
   506      BOOL ok = MoveFileExW(src2, target2, MOVEFILE_REPLACE_EXISTING);
   507      free(src2);
   508      free(target2);
   509      if (!ok)
   510          return IOError("RenameFile " + src + " " + target);
   511      return Status::OK();
   512    }
   513  
   514    virtual Status LockFile(const std::string& fname, FileLock** lock) {
   515      *lock = NULL;
   516      WCHAR *file_name = ToWcharPermissive(fname.c_str());
   517      if (file_name == NULL) {
   518        return Status::InvalidArgument("Invalid file name");
   519      }
   520      HANDLE h = CreateFileW(file_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
   521      free((void*)file_name);
   522      if (h == INVALID_HANDLE_VALUE) {
   523        return IOError("LockFile " + fname);
   524      }
   525      *lock = new WinFileLock(fname, h);
   526      return Status::OK();
   527    }
   528  
   529    virtual Status UnlockFile(FileLock* lock) {
   530      Status s;
   531      WinFileLock* my_lock = reinterpret_cast<WinFileLock*>(lock);
   532      if (!my_lock->Close()) {
   533        s = Status::IOError(my_lock->fname_, "Could not close lock file.");
   534      }
   535      delete my_lock;
   536      return Status::OK();
   537    }
   538  
   539    virtual void Schedule(void (*function)(void*), void* arg);
   540  
   541    virtual void StartThread(void (*function)(void* arg), void* arg);
   542  
   543    virtual Status GetTestDirectory(std::string* result) {
   544      WCHAR buf[MAX_PATH];
   545      DWORD res = GetTempPathW(MAX_PATH, buf);
   546      if (0 == res) {
   547        return IOError("Can't get test directory");
   548      }
   549      char *s = ToUtf8(buf);
   550      if (!s) {
   551        return IOError("Can't get test directory");
   552      }
   553      *result = std::string(s);
   554      free(s);
   555      return Status::OK();
   556    }
   557  
   558    virtual Status NewLogger(const std::string& fname, Logger** result) {
   559      *result = NULL;
   560      FILE* f = fopen(fname.c_str(), "wt");
   561      if (f == NULL)
   562        return Status::IOError(fname, strerror(errno));
   563      *result = new WinLogger(f);
   564      return Status::OK();
   565    }
   566  
   567    virtual uint64_t NowMicros() {
   568      LARGE_INTEGER count;
   569      QueryPerformanceCounter(&count);
   570      return count.QuadPart * 1000000LL / freq_.QuadPart;
   571    }
   572  
   573    virtual void SleepForMicroseconds(int micros) {
   574       // round up to the next millisecond
   575      Sleep((micros + 999) / 1000);
   576    }
   577  
   578  private:
   579    LARGE_INTEGER freq_;
   580  
   581    // BGThread() is the body of the background thread
   582    void BGThread();
   583  
   584    static unsigned __stdcall BGThreadWrapper(void* arg) {
   585      (reinterpret_cast<WinEnv*>(arg))->BGThread();
   586      _endthreadex(0);
   587      return 0;
   588    }
   589  
   590    leveldb::port::Mutex mu_;
   591    leveldb::port::CondVar bgsignal_;
   592    HANDLE  bgthread_;
   593  
   594    // Entry per Schedule() call
   595    struct BGItem { void* arg; void (*function)(void*); };
   596    typedef std::deque<BGItem> BGQueue;
   597    BGQueue queue_;
   598  };
   599  
   600  
   601  WinEnv::WinEnv() : bgthread_(NULL), bgsignal_(&mu_) {
   602    QueryPerformanceFrequency(&freq_);
   603  }
   604  
   605  void WinEnv::Schedule(void (*function)(void*), void* arg) {
   606    mu_.Lock();
   607  
   608    // Start background thread if necessary
   609    if (NULL == bgthread_) {
   610      bgthread_ = (HANDLE)_beginthreadex(NULL, 0, &WinEnv::BGThreadWrapper, this, 0, NULL);
   611    }
   612  
   613    // Add to priority queue
   614    queue_.push_back(BGItem());
   615    queue_.back().function = function;
   616    queue_.back().arg = arg;
   617  
   618    mu_.Unlock();
   619  
   620    bgsignal_.Signal();
   621  }
   622  
   623  void WinEnv::BGThread() {
   624    while (true) {
   625      // Wait until there is an item that is ready to run
   626      mu_.Lock();
   627  
   628      while (queue_.empty()) {
   629        bgsignal_.Wait();
   630      }
   631  
   632      void (*function)(void*) = queue_.front().function;
   633      void* arg = queue_.front().arg;
   634      queue_.pop_front();
   635  
   636      mu_.Unlock();
   637      (*function)(arg);
   638    }
   639    // TODO: CloseHandle(bgthread_) ??
   640  }
   641  
   642  namespace {
   643  struct StartThreadState {
   644    void (*user_function)(void*);
   645    void* arg;
   646    HANDLE threadHandle;
   647  };
   648  }
   649  
   650  static unsigned __stdcall StartThreadWrapper(void* arg) {
   651    StartThreadState* state = reinterpret_cast<StartThreadState*>(arg);
   652    state->user_function(state->arg);
   653    _endthreadex(0);
   654    CloseHandle(state->threadHandle);
   655    delete state;
   656    return 0;
   657  }
   658  
   659  void WinEnv::StartThread(void (*function)(void* arg), void* arg) {
   660    StartThreadState* state = new StartThreadState;
   661    state->user_function = function;
   662    state->arg = arg;
   663    state->threadHandle = (HANDLE)_beginthreadex(NULL, 0, &StartThreadWrapper, state, 0, NULL);
   664  }
   665  }
   666  
   667  static Env* default_env;
   668  static void InitDefaultEnv() { default_env = new WinEnv(); }
   669  static leveldb::port::Mutex default_env_mutex;
   670  
   671  Env* Env::Default() {
   672    default_env_mutex.Lock();
   673    if (NULL == default_env)
   674      InitDefaultEnv();
   675    default_env_mutex.Unlock();
   676    return default_env;
   677  }
   678  
   679  }