public void _memcpy(VSystem ct) { ct.SizeOsFile = this.SizeOsFile; ct.MaxPathname = this.MaxPathname; ct.Next = this.Next; ct.Name = this.Name; ct.Tag = this.Tag; }
public static RC UnregisterVfs(VSystem vfs) { var mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); MutexEx.Enter(mutex); UnlinkVfs(vfs); MutexEx.Leave(mutex); return(RC.OK); }
public static VSystem FindVfs(string name) { VSystem vfs = null; var mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); MutexEx.Enter(mutex); for (vfs = _vfsList; vfs != null && name != vfs.Name; vfs = vfs.Next) { } MutexEx.Leave(mutex); return(vfs); }
public static RC RegisterVfs(VSystem vfs, bool @default, Func<VFile> createOsFile) { var mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); MutexEx.Enter(mutex); UnlinkVfs(vfs); vfs.CreateOsFile = createOsFile; if (@default || _vfsList == null) { vfs.Next = _vfsList; _vfsList = vfs; } else { vfs.Next = _vfsList.Next; _vfsList.Next = vfs; } Debug.Assert(_vfsList != null); MutexEx.Leave(mutex); return RC.OK; }
public static RC Shutdown() { if (_GlobalStatics.IsInit) { VSystem.Shutdown(); //sqlite3_reset_auto_extension(); _GlobalStatics.IsInit = false; } //if (SysEx_GlobalStatics.IsMallocInit) //{ // sqlite3MallocEnd(); // SysEx_GlobalStatics.IsMallocInit = false; //} if (_GlobalStatics.IsMutexInit) { //MutexEx_End(); _GlobalStatics.IsMutexInit = false; } return(RC.OK); }
public static RC RegisterVfs(VSystem vfs, bool default_, Func <VFile> createOsFile) { var mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); MutexEx.Enter(mutex); UnlinkVfs(vfs); vfs.CreateOsFile = createOsFile; if (default_ || _vfsList == null) { vfs.Next = _vfsList; _vfsList = vfs; } else { vfs.Next = _vfsList.Next; _vfsList.Next = vfs; } Debug.Assert(_vfsList != null); MutexEx.Leave(mutex); return(RC.OK); }
public static byte RandomByte() { // Initialize the state of the random number generator once, the first time this routine is called. The seed value does // not need to contain a lot of randomness since we are not trying to do secure encryption or anything like that... // // Nothing in this file or anywhere else in SQLite does any kind of encryption. The RC4 algorithm is being used as a PRNG (pseudo-random // number generator) not as an encryption device. byte t; if (!_prng.IsInit) { byte[] k = new byte[256]; _prng.J = 0; _prng.I = 0; VSystem.FindVfs(string.Empty).Randomness(256, k); int i; for (i = 0; i < 255; i++) { _prng.S[i] = (byte)i; } for (i = 0; i < 255; i++) { _prng.J += _prng.S[i] + k[i]; t = _prng.S[_prng.J]; _prng.S[_prng.J] = _prng.S[i]; _prng.S[i] = t; } _prng.IsInit = true; } // Generate and return single random u8 _prng.I++; t = _prng.S[_prng.I]; _prng.J += t; _prng.S[_prng.I] = _prng.S[_prng.J]; _prng.S[_prng.J] = t; t += _prng.S[_prng.I]; return(_prng.S[t]); }
static Pager Open(VSystem vfs) { var dbHeader = new byte[100]; // Database header content IPager.PAGEROPEN flags = 0; var vfsFlags = VSystem.OPEN.CREATE | VSystem.OPEN.READWRITE | VSystem.OPEN.MAIN_DB; // Pager pager; var rc = Pager.Open(vfs, out pager, @"C:\T_\Test.db", 0, flags, vfsFlags, x => { }, null); if (rc == RC.OK) rc = pager.ReadFileHeader(dbHeader.Length, dbHeader); if (rc != RC.OK) goto _out; pager.SetBusyHandler(BusyHandler, null); var readOnly = pager.get_Readonly(); // int reserves; var pageSize = (uint)((dbHeader[16] << 8) | (dbHeader[17] << 16)); if (pageSize < 512 || pageSize > Pager.MAX_PAGE_SIZE || ((pageSize - 1) & pageSize) != 0) { pageSize = 0; reserves = 0; } else reserves = dbHeader[20]; rc = pager.SetPageSize(ref pageSize, reserves); if (rc != RC.OK) goto _out; _out: if (rc != RC.OK) { if (pager != null) pager.Close(); pager = null; } else pager.SetCacheSize(2000); return pager; }
internal static void UnlinkVfs(VSystem vfs) { Debug.Assert(MutexEx.Held(MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER))); if (vfs == null) { } else if (_vfsList == vfs) { _vfsList = vfs.Next; } else if (_vfsList != null) { var p = _vfsList; while (p.Next != null && p.Next != vfs) { p = p.Next; } if (p.Next == vfs) { p.Next = vfs.Next; } } }
public static RC Open(VSystem vfs, string filename, BContext ctx, ref Btree btree, OPEN flags, VSystem.OPEN vfsFlags) { // True if opening an ephemeral, temporary database bool tempDB = string.IsNullOrEmpty(filename); // Set the variable isMemdb to true for an in-memory database, or false for a file-based database. bool memoryDB = (filename == ":memory:") || (tempDB && ctx.TempInMemory()) || (vfsFlags & VSystem.OPEN.MEMORY) != 0; Debug.Assert(ctx != null); Debug.Assert(vfs != null); Debug.Assert(MutexEx.Held(ctx.Mutex)); Debug.Assert(((uint)flags & 0xff) == (uint)flags); // flags fit in 8 bits // Only a BTREE_SINGLE database can be BTREE_UNORDERED Debug.Assert((flags & OPEN.UNORDERED) == 0 || (flags & OPEN.SINGLE) != 0); // A BTREE_SINGLE database is always a temporary and/or ephemeral Debug.Assert((flags & OPEN.SINGLE) == 0 || tempDB); if (memoryDB) flags |= OPEN.MEMORY; if ((vfsFlags & VSystem.OPEN.MAIN_DB) != 0 && (memoryDB || tempDB)) vfsFlags = (vfsFlags & ~VSystem.OPEN.MAIN_DB) | VSystem.OPEN.TEMP_DB; var p = new Btree(); // Handle to return if (p == null) return RC.NOMEM; p.InTrans = TRANS.NONE; p.Ctx = ctx; #if !OMIT_SHARED_CACHE p.Lock.Btree = p; p.Lock.Table = 1; #endif RC rc = RC.OK; // Result code from this function BtShared bt = null; // Shared part of btree structure MutexEx mutexOpen = null; #if !OMIT_SHARED_CACHE && !OMIT_DISKIO // If this Btree is a candidate for shared cache, try to find an existing BtShared object that we can share with if (!tempDB && (!memoryDB || (vfsFlags & VSystem.OPEN.URI) != 0)) if ((vfsFlags & VSystem.OPEN.SHAREDCACHE) != 0) { string fullPathname; p.Sharable_ = true; if (memoryDB) fullPathname = filename; else vfs.FullPathname(filename, out fullPathname); MutexEx mutexShared; #if THREADSAFE mutexOpen = MutexEx.Alloc(MutexEx.MUTEX.STATIC_OPEN); // Prevents a race condition. Ticket #3537 MutexEx.Enter(mutexOpen); mutexShared = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); MutexEx.Enter(mutexShared); #endif for (bt = _sharedCacheList; bt != null; bt = bt.Next) { Debug.Assert(bt.Refs > 0); if (fullPathname == bt.Pager.get_Filename(false) && bt.Pager.get_Vfs() == vfs) { for (var i = ctx.DBs.length - 1; i >= 0; i--) { var existing = ctx.DBs[i].Bt; if (existing != null && existing.Bt == bt) { MutexEx.Leave(mutexShared); MutexEx.Leave(mutexOpen); fullPathname = null; p = null; return RC.CONSTRAINT; } } p.Bt = bt; bt.Refs++; break; } } MutexEx.Leave(mutexShared); fullPathname = null; } #if DEBUG else // In debug mode, we mark all persistent databases as sharable even when they are not. This exercises the locking code and // gives more opportunity for asserts(sqlite3_mutex_held()) statements to find locking problems. p.Sharable_ = true; #endif #endif byte reserves; // Byte of unused space on each page var dbHeader = new byte[100]; // Database header content if (bt == null) { // The following asserts make sure that structures used by the btree are the right size. This is to guard against size changes that result // when compiling on a different architecture. Debug.Assert(sizeof(long) == 8 || sizeof(long) == 4); Debug.Assert(sizeof(ulong) == 8 || sizeof(ulong) == 4); Debug.Assert(sizeof(uint) == 4); Debug.Assert(sizeof(ushort) == 2); Debug.Assert(sizeof(Pid) == 4); bt = new BtShared(); if (bt == null) { rc = RC.NOMEM; goto btree_open_out; } rc = Pager.Open(vfs, out bt.Pager, filename, EXTRA_SIZE, (IPager.PAGEROPEN)flags, vfsFlags, pageReinit, null); if (rc == RC.OK) rc = bt.Pager.ReadFileHeader(dbHeader.Length, dbHeader); if (rc != RC.OK) goto btree_open_out; bt.OpenFlags = flags; bt.Ctx = ctx; bt.Pager.SetBusyHandler(btreeInvokeBusyHandler, bt); p.Bt = bt; bt.Cursor = null; bt.Page1 = null; if (bt.Pager.get_Readonly()) bt.BtsFlags |= BTS.READ_ONLY; #if SECURE_DELETE bt.BtsFlags |= BTS.SECURE_DELETE; #endif bt.PageSize = (Pid)((dbHeader[16] << 8) | (dbHeader[17] << 16)); if (bt.PageSize < 512 || bt.PageSize > Pager.MAX_PAGE_SIZE || ((bt.PageSize - 1) & bt.PageSize) != 0) { bt.PageSize = 0; #if !OMIT_AUTOVACUUM // If the magic name ":memory:" will create an in-memory database, then leave the autoVacuum mode at 0 (do not auto-vacuum), even if // SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a // regular file-name. In this case the auto-vacuum applies as per normal. if (filename != null && !memoryDB) { bt.AutoVacuum = (DEFAULT_AUTOVACUUM != 0); bt.IncrVacuum = (DEFAULT_AUTOVACUUM == AUTOVACUUM.INCR); } #endif reserves = 0; } else { reserves = dbHeader[20]; bt.BtsFlags |= BTS.PAGESIZE_FIXED; #if !OMIT_AUTOVACUUM bt.AutoVacuum = (ConvertEx.Get4(dbHeader, 36 + 4 * 4) != 0); bt.IncrVacuum = (ConvertEx.Get4(dbHeader, 36 + 7 * 4) != 0); #endif } rc = bt.Pager.SetPageSize(ref bt.PageSize, reserves); if (rc != RC.OK) goto btree_open_out; bt.UsableSize = (ushort)(bt.PageSize - reserves); Debug.Assert((bt.PageSize & 7) == 0); // 8-byte alignment of pageSize #if !SHARED_CACHE && !OMIT_DISKIO // Add the new BtShared object to the linked list sharable BtShareds. if (p.Sharable_) { bt.Refs = 1; MutexEx mutexShared; #if THREADSAFE mutexShared = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); bt.Mutex = MutexEx.Alloc(MutexEx.MUTEX.FAST); #endif MutexEx.Enter(mutexShared); bt.Next = _sharedCacheList; _sharedCacheList = bt; MutexEx.Leave(mutexShared); } #endif } #if !OMIT_SHARED_CACHE && !OMIT_DISKIO // If the new Btree uses a sharable pBtShared, then link the new Btree into the list of all sharable Btrees for the same connection. // The list is kept in ascending order by pBt address. if (p.Sharable_) { Btree sib; for (var i = 0; i < ctx.DBs.length; i++) if ((sib = ctx.DBs[i].Bt) != null && sib.Sharable_) { while (sib.Prev != null) { sib = sib.Prev; } if (p.Bt.AutoID < sib.Bt.AutoID) { p.Next = sib; p.Prev = null; sib.Prev = p; } else { while (sib.Next != null && sib.Next.Bt.AutoID < p.Bt.AutoID) sib = sib.Next; p.Next = sib.Next; p.Prev = sib; if (p.Next != null) p.Next.Prev = p; sib.Next = p; } break; } } #endif btree = p; btree_open_out: if (rc != RC.OK) { if (bt != null && bt.Pager != null) bt.Pager.Close(); bt = null; p = null; btree = null; } else // If the B-Tree was successfully opened, set the pager-cache size to the default value. Except, when opening on an existing shared pager-cache, // do not change the pager-cache size. if (p.Schema(0, null) == null) p.Bt.Pager.SetCacheSize(DEFAULT_CACHE_SIZE); #if THREADSAFE Debug.Assert(MutexEx.Held(mutexOpen)); MutexEx.Leave(mutexOpen); #endif return rc; }
public static RC LoadExtension_(Context ctx, string fileName, string procName, ref string errMsgOut) { if (errMsgOut != null) { errMsgOut = null; } // Ticket #1863. To avoid a creating security problems for older applications that relink against newer versions of SQLite, the // ability to run load_extension is turned off by default. One must call core_enable_load_extension() to turn on extension // loading. Otherwise you get the following error. if ((ctx.Flags & Context.FLAG.LoadExtension) == 0) { errMsgOut = C._mprintf("not authorized"); return(RC.ERROR); } if (procName == null) { procName = "sqlite3_extension_init"; } VSystem vfs = ctx.Vfs; HANDLE handle = (HANDLE)vfs.DlOpen(fileName); StringBuilder errmsg = new StringBuilder(100); int msgLength = 300; if (handle == IntPtr.Zero) { errMsgOut = string.Empty; C.__snprintf(errmsg, msgLength, "unable to open shared library [%s]", fileName); vfs.DlError(msgLength - 1, errmsg.ToString()); return(RC.ERROR); } Func <Context, StringBuilder, core_api_routines, RC> init = (Func <Context, StringBuilder, core_api_routines, RC>)vfs.DlSym(handle, procName); Debugger.Break(); if (init == null) { msgLength += procName.Length; C.__snprintf(errmsg, msgLength, "no entry point [%s] in shared library [%s]", procName, fileName); vfs.DlError(msgLength - 1, errMsgOut = errmsg.ToString()); vfs.DlClose(handle); return(RC.ERROR); } else if (init(ctx, errmsg, g_apis) != 0) { errMsgOut = C._mprintf("error during initialization: %s", errmsg.ToString()); C._tagfree(ctx, ref errmsg); vfs.DlClose(handle); return(RC.ERROR); } // Append the new shared library handle to the db.aExtension array. object[] handles = new object[ctx.Extensions.length + 1]; if (handles == null) { return(RC.NOMEM); } if (ctx.Extensions.length > 0) { Array.Copy(ctx.Extensions.data, handles, ctx.Extensions.length); } C._tagfree(ctx, ref ctx.Extensions.data); ctx.Extensions.data = handles; ctx.Extensions[ctx.Extensions.length++] = handle; return(RC.OK); }
internal static void UnlinkVfs(VSystem vfs) { Debug.Assert(MutexEx.Held(MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER))); if (vfs == null) { } else if (_vfsList == vfs) _vfsList = vfs.Next; else if (_vfsList != null) { var p = _vfsList; while (p.Next != null && p.Next != vfs) p = p.Next; if (p.Next == vfs) p.Next = vfs.Next; } }
public static RC UnregisterVfs(VSystem vfs) { var mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); MutexEx.Enter(mutex); UnlinkVfs(vfs); MutexEx.Leave(mutex); return RC.OK; }
public static RC ParseUri(string defaultVfsName, string uri, ref VSystem.OPEN flagsRef, out VSystem vfsOut, out string fileNameOut, out string errMsgOut) { vfsOut = null; fileNameOut = null; errMsgOut = null; VSystem.OPEN flags = flagsRef; string vfsName = defaultVfsName; int uriLength = uri.Length; RC rc = RC.OK; StringBuilder fileName = null; if (((flags & VSystem.OPEN.URI) != 0 || SysEx._GlobalStatics.OpenUri) && uriLength >= 5 && uri.StartsWith("file:")) { // Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen method that there may be extra parameters following the file-name. flags |= VSystem.OPEN.URI; int bytes = uriLength + 2; // Bytes of space to allocate int uriIdx; // Input character index for (uriIdx = 0; uriIdx < uriLength; uriIdx++) { bytes += (uri[uriIdx] == '&' ? 1 : 0); } fileName = new StringBuilder(bytes); if (fileName == null) { return(RC.NOMEM); } // Discard the scheme and authority segments of the URI. if (uri[5] == '/' && uri[6] == '/') { uriIdx = 7; while (uriIdx < uriLength && uri[uriIdx] != '/') { uriIdx++; } if (uriIdx != 7 && (uriIdx != 16 || !string.Equals("localhost", uri.Substring(7, 9), StringComparison.InvariantCultureIgnoreCase))) { errMsgOut = C._mprintf("invalid uri authority: %.*s", uriIdx - 7, uri.Substring(7)); rc = RC.ERROR; goto parse_uri_out; } } else { uriIdx = 5; } // Copy the filename and any query parameters into the zFile buffer. Decode %HH escape codes along the way. // // Within this loop, variable eState may be set to 0, 1 or 2, depending on the parsing context. As follows: // // 0: Parsing file-name. // 1: Parsing name section of a name=value query parameter. // 2: Parsing value section of a name=value query parameter. int state = 0; // Parser state when parsing URI char c; //int fileNameIdx = 0; // Output character index while (uriIdx < uriLength && (c = uri[uriIdx]) != 0 && c != '#') { uriIdx++; if (c == '%' && C._isxdigit(uri[uriIdx]) && C._isxdigit(uri[uriIdx + 1])) { int octet = (C._hextobyte(uri[uriIdx++]) << 4); octet += C._hextobyte(uri[uriIdx++]); Debug.Assert(octet >= 0 && octet < 256); if (octet == 0) { // This branch is taken when "%00" appears within the URI. In this case we ignore all text in the remainder of the path, name or // value currently being parsed. So ignore the current character and skip to the next "?", "=" or "&", as appropriate. while (uriIdx < uriLength && (c = uri[uriIdx]) != 0 && c != '#' && (state != 0 || c != '?') && (state != 1 || (c != '=' && c != '&')) && (state != 2 || c != '&')) { uriIdx++; } continue; } c = (char)octet; } else if (state == 1 && (c == '&' || c == '=')) { if (fileName[fileName.Length - 1] == '\0') { // An empty option name. Ignore this option altogether. while (uri[uriIdx] != '\0' && uri[uriIdx] != '#' && uri[uriIdx - 1] != '&') { uriIdx++; } continue; } if (c == '&') { fileName.Append('\0'); } else { state = 2; } c = '\0'; } else if ((state == 0 && c == '?') || (state == 2 && c == '&')) { c = '\0'; state = 1; } fileName.Append(c); } if (state == 1) { fileName.Append('\0'); } fileName.Append('\0'); fileName.Append('\0'); // Check if there were any options specified that should be interpreted here. Options that are interpreted here include "vfs" and those that // correspond to flags that may be passed to the sqlite3_open_v2() method. string opt = fileName.ToString().Substring(fileName.Length + 1); while (opt.Length > 0) { int optLength = opt.Length; string val = opt.Substring(optLength); int valLength = val.Length; if (optLength == 3 && opt.StartsWith("vfs")) { vfsName = val; } else { OpenMode[] modes = null; string modeType = null; VSystem.OPEN mask = (VSystem.OPEN) 0; VSystem.OPEN limit = (VSystem.OPEN) 0; if (optLength == 5 && opt.StartsWith("cache")) { mask = VSystem.OPEN.SHAREDCACHE | VSystem.OPEN.PRIVATECACHE; modes = _cacheModes; limit = mask; modeType = "cache"; } if (optLength == 4 && opt.StartsWith("mode")) { mask = VSystem.OPEN.READONLY | VSystem.OPEN.READWRITE | VSystem.OPEN.CREATE; modes = _openModes; limit = mask & flags; modeType = "access"; } if (modes != null) { VSystem.OPEN mode = 0; for (int i = 0; modes[i].Z != null; i++) { string z = modes[i].Z; if (valLength == z.Length && z.StartsWith(val)) { mode = modes[i].Mode; break; } } if (mode == 0) { errMsgOut = C._mprintf("no such %s mode: %s", modeType, val); rc = RC.ERROR; goto parse_uri_out; } if (mode > limit) { errMsgOut = C._mprintf("%s mode not allowed: %s", modeType, val); rc = RC.PERM; goto parse_uri_out; } flags = ((flags & ~mask) | mode); } } opt = val.Substring(valLength + 1); } } else { fileName = (uri == null ? new StringBuilder() : new StringBuilder(uri.Substring(0, uriLength))); if (fileName == null) { return(RC.NOMEM); } fileName.Append('\0'); fileName.Append('\0'); } vfsOut = FindVfs(vfsName); if (vfsOut == null) { errMsgOut = C._mprintf("no such vfs: %s", vfsName); rc = RC.ERROR; } parse_uri_out: if (rc != RC.OK) { C._free(ref fileName); fileName = null; } flagsRef = flags; fileNameOut = (fileName == null ? null : fileName.ToString().Substring(0, fileName.Length)); return(rc); }
internal static RC Open(VSystem x, VFile y, string z) { return(RC.OK); }
internal static RC Open(VSystem x, VFile y, string z) { return RC.OK; }
public void CopyTo(VSystem ct) { ct.SizeOsFile = this.SizeOsFile; ct.MaxPathname = this.MaxPathname; ct.Next = this.Next; ct.Name = this.Name; ct.Tag = this.Tag; }
public static RC PreInitialize(out MutexEx masterMutex) { masterMutex = default(MutexEx); // If SQLite is already completely initialized, then this call to sqlite3_initialize() should be a no-op. But the initialization // must be complete. So isInit must not be set until the very end of this routine. if (_GlobalStatics.IsInit) { return(RC.OK); } // The following is just a sanity check to make sure SQLite has been compiled correctly. It is important to run this code, but // we don't want to run it too often and soak up CPU cycles for no reason. So we run it once during initialization. #if !NDEBUG && !OMIT_FLOATING_POINT // This section of code's only "output" is via assert() statements. //ulong x = (((ulong)1)<<63)-1; //double y; //Debug.Assert(sizeof(ulong) == 8); //Debug.Assert(sizeof(ulong) == sizeof(double)); //_memcpy<void>(&y, &x, 8); //Debug.Assert(double.IsNaN(y)); #endif RC rc; #if ENABLE_SQLLOG { Init_Sqllog(); } #endif // Make sure the mutex subsystem is initialized. If unable to initialize the mutex subsystem, return early with the error. // If the system is so sick that we are unable to allocate a mutex, there is not much SQLite is going to be able to do. // The mutex subsystem must take care of serializing its own initialization. rc = RC.OK; //_mutex_init(); if (rc != 0) { return(rc); } // Initialize the malloc() system and the recursive pInitMutex mutex. This operation is protected by the STATIC_MASTER mutex. Note that // MutexAlloc() is called for a static mutex prior to initializing the malloc subsystem - this implies that the allocation of a static // mutex must not require support from the malloc subsystem. masterMutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); // The main static mutex MutexEx.Enter(masterMutex); _GlobalStatics.IsMutexInit = true; //if (!SysEx_GlobalStatics.IsMallocInit) // rc = sqlite3MallocInit(); if (rc == RC.OK) { _GlobalStatics.IsMallocInit = true; if (_GlobalStatics.InitMutex.Tag == null) { _GlobalStatics.InitMutex = MutexEx.Alloc(MutexEx.MUTEX.RECURSIVE); if (_GlobalStatics.CoreMutex && _GlobalStatics.InitMutex.Tag == null) { rc = RC.NOMEM; } } } if (rc == RC.OK) { _GlobalStatics.InitMutexRefs++; } MutexEx.Leave(masterMutex); // If rc is not SQLITE_OK at this point, then either the malloc subsystem could not be initialized or the system failed to allocate // the pInitMutex mutex. Return an error in either case. if (rc != RC.OK) { return(rc); } // Do the rest of the initialization under the recursive mutex so that we will be able to handle recursive calls into // sqlite3_initialize(). The recursive calls normally come through sqlite3_os_init() when it invokes sqlite3_vfs_register(), but other // recursive calls might also be possible. // // IMPLEMENTATION-OF: R-00140-37445 SQLite automatically serializes calls to the xInit method, so the xInit method need not be threadsafe. // // The following mutex is what serializes access to the appdef pcache xInit methods. The sqlite3_pcache_methods.xInit() all is embedded in the // call to sqlite3PcacheInitialize(). MutexEx.Enter(_GlobalStatics.InitMutex); if (!_GlobalStatics.IsInit && !_GlobalStatics.InProgress) { _GlobalStatics.InProgress = true; rc = VSystem.Initialize(); } if (rc != RC.OK) { MutexEx.Leave(_GlobalStatics.InitMutex); } return(rc); }