-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[QUESTION] Object synchronization method was called from an unsynchronized block of code #1546
Comments
@jokker23 Could you please provide us with more information like, which version of LiteDB you're using, OS, net runtime, et cetera 🙂 |
Certainly, LiteDB: v5.0.2 Also using Is there other information that is interesting? |
@jokker23 Could you provide a sample code that we may use to replicate the issue? |
@jokker23 Could you test with the current master? The transaction model was entirely rewritten. |
Yes sorry I will try to accommodate both requests as soon as possible. I'm currently on overload making VPN tunnels for home workers, sorry for the delay. |
I got the same error, too.
|
@alonstar What version are you running? |
5.0.4 and .net framework 4.7.2 |
I've the same problem with 5.0.5 on .Net core 3.1 when multiple API calls are trying to connect to the same database. Update 1: I've changed my Autofac configuration so it will always create a new provider (with internally a new LiteDb instance). Update 2: |
I can reliably reproduce this exception (not necessarily same issue) with the [Fact]
public void Just_DataStream3()
{
using var mem = new MemoryStream();
var settings = new EngineSettings()
{
DataStream = mem,
Filename = "Just_DataStream3" //! see https://github.com/mbdavid/LiteDB/issues/1614
};
using var engine = new SharedEngine(settings);
void worker()
{
using var db = new LiteDatabase(engine, null, false);
var col = db.GetCollection("test");
for (int i = 0; i < 100; ++i)
{
col.Insert(new BsonDocument() { ["i"] = i });
System.Threading.Thread.Sleep(50);
}
}
var thread1 = new System.Threading.Thread(worker);
var thread2 = new System.Threading.Thread(worker);
var thread3 = new System.Threading.Thread(worker);
thread1.Start();
thread2.Start();
thread3.Start();
thread1.Join();
thread2.Join();
thread3.Join();
using var db = new LiteDatabase(engine, null, false);
var col = db.GetCollection("test");
Assert.Equal(300, col.Count());
} |
Can you repro the problem with the above code? Please let me know. If not then I will continue the search for repro cases. I think problems might be related to #1614 which is not well repro, too. |
@nightroman Yes, I was able to reproduce the problem. |
Any updates? Having the same issue :( |
Is there any progress regarding this issue or at least an idea what is causing the issue? |
@jokker23 @alonstar @mwlpeeters @nightroman @Sn3b @temnava I tried replacing None of the solutions is perfect, and I'm not sure which is preferable, but I'm tempted to keep the Shared mode as it currently is. It will be completely reworked for v5.1 anyway. |
Hello, than you very much for a fast response and this great library. |
@temnava Here's a pastebin with the code that uses |
@lbnascimento Edit: I use a connection string with Connection=shared because I also have another service that needs to access that same DB. |
So if i got this correct, there is a problem with concurrent access when multiple threads do operations on the database, because the the mutex must be released by the thread which took the lock, and the lock is opened when the count is 1 and closed when it goes back to 0, meaning the "last" thread active closes it. So imo the mutex is useless, as it doesn't make the application threadsafe. Currently the user is responsible for ensuring there is no concurrent access, making the mutex excessive. So in this case I think it's quite obvious that the advantages of the semaphore (actually being threadsafe) outweigh the advantage of knowing when a mutex is abandoned massively. *Edit: *Edit 2: |
@MidasLamb The |
@lbnascimento I noticed that when I was creating a PR. Do you have an ETA for this change? Currently we're just surrounding all the accesses with a lock statement to prevent the error from being thrown. |
Hello, is there any progress in resolving this issue? |
I've faced the same issue with a lot of concurrent calls when using Shared connection mode. |
I'm hitting this on changing to shared connection in various places but seems in my data access layer code, using a lock around the code as suggested above and returning concrete lists with ToList for IEnumerable results seems to help. It means the DB read occurs at that point, not in subsequent LINQ queries. I'm reviewing using shared connection though and thinking of other ways with multi-process, aside from a full database (some issues blocking that with some customer requirements and limits on installed database products, especially if they have to maintain them). My initial query on #1773 maybe is answered by a server based LiteDB though, if it can be embedded in some way. |
I'm getting the same error with Shared Engine. System.ApplicationException: Object synchronization method was called from an unsynchronized block of code. And then, I tried to patch it, debugging for two weeks. It's working fine for me now. LiteDB/Client/Shared/SharedEngine.cs using LiteDB.Engine;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
#if NETFRAMEWORK
using System.Security.AccessControl;
using System.Security.Principal;
#endif
namespace LiteDB
{
public class SharedEngine : ILiteEngine
{
private readonly EngineSettings _settings;
private readonly Mutex _mutex;
private LiteEngine _engine;
private Random rand = new Random(
Guid.NewGuid().GetHashCode()
);
//Thread safe bool, default is 0 as false;
private volatile int _threadSafeBoolBackValueForIsMutexLocking = 0;
private bool isMutexLocking
{
get
{
return (Interlocked.CompareExchange(ref _threadSafeBoolBackValueForIsMutexLocking, 1, 1) == 1);
}
set
{
if (value) Interlocked.CompareExchange(ref _threadSafeBoolBackValueForIsMutexLocking, 1, 0);
else Interlocked.CompareExchange(ref _threadSafeBoolBackValueForIsMutexLocking, 0, 1);
}
}
//Thread safe bool, default is 0 as false;
private volatile int _threadSafeBoolBackValueForIsDisposed = 0;
private bool isDisposed
{
get
{
return (Interlocked.CompareExchange(ref _threadSafeBoolBackValueForIsDisposed, 1, 1) == 1);
}
set
{
if (value) Interlocked.CompareExchange(ref _threadSafeBoolBackValueForIsDisposed, 1, 0);
else Interlocked.CompareExchange(ref _threadSafeBoolBackValueForIsDisposed, 0, 1);
}
}
public SharedEngine(EngineSettings settings)
{
_settings = settings;
var name = Path.GetFullPath(settings.Filename).ToLower().Sha1();
try
{
#if NETFRAMEWORK
var allowEveryoneRule = new MutexAccessRule(
new SecurityIdentifier(WellKnownSidType.WorldSid, null),
MutexRights.FullControl, AccessControlType.Allow);
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);
_mutex = new Mutex(false, "Global\\" + name + ".Mutex", out _, securitySettings);
#else
_mutex = new Mutex(false, "Global\\" + name + ".Mutex");
#endif
}
catch (NotSupportedException ex)
{
throw new PlatformNotSupportedException
("Shared mode is not supported in platforms that do not implement named mutex.", ex);
}
}
/// <summary>
/// Open database in safe mode
/// </summary>
private void OpenDatabase()
{
if (!isMutexLocking)
{
lock (_mutex)
{
isMutexLocking = true;
if (_engine == null)
{
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
try
{
_engine = new LiteEngine(_settings);
isDisposed = false;
}
catch
{
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
}
isMutexLocking = false;
}
}
else
{
if (_engine == null)
{
try
{
_engine = new LiteEngine(_settings);
isDisposed = false;
}
catch
{
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
}
}
}
/// <summary>
/// Dequeue stack and dispose database on empty stack
/// </summary>
private void CloseDatabase()
{
if (!isMutexLocking)
{
lock (_mutex)
{
isMutexLocking = true;
if (_engine != null)
{
this.Dispose(true);
}
isMutexLocking = false;
}
}
else
{
if (_engine != null)
{
this.Dispose(true);
}
}
}
#region Transaction Operations
public bool BeginTrans()
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
bool val = false;
try
{
var result = _engine.BeginTrans();
/*
if(result == false)
{
}
*/
val = result;
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public bool Commit()
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
if (_engine == null || isDisposed)
{
isDisposed = true;
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return false;
}
bool val = false;
try
{
val = _engine.Commit();
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public bool Rollback()
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
if (_engine == null || isDisposed)
{
isDisposed = true;
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return false;
}
bool val = false;
try
{
val = _engine.Rollback();
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
#endregion
#region Read Operation
public IBsonDataReader Query(string collection, Query query)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
SharedDataReader bsonDataReader = null;
try
{
var reader = _engine.Query(collection, query);
bsonDataReader = new SharedDataReader(reader, () => this.Dispose("Query"));
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
if(bsonDataReader != null)
{
return bsonDataReader;
}
else
{
throw new NullReferenceException("Nulled SharedDataReader.");
}
}
}
public BsonValue Pragma(string name)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
BsonValue val = null;
try
{
val = _engine.Pragma(name);
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public bool Pragma(string name, BsonValue value)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
bool val = false;
try
{
val = _engine.Pragma(name, value);
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
#endregion
#region Write Operations
public int Checkpoint()
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
int val;
try
{
val = _engine.Checkpoint();
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public long Rebuild(RebuildOptions options)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
long val;
try
{
val = _engine.Rebuild(options);
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public int Insert(string collection, IEnumerable<BsonDocument> docs, BsonAutoId autoId)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
int val;
try
{
val = _engine.Insert(collection, docs, autoId);
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public int Update(string collection, IEnumerable<BsonDocument> docs)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
int val;
try
{
val = _engine.Update(collection, docs);
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public int UpdateMany(string collection, BsonExpression extend, BsonExpression predicate)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
int val;
try
{
val = _engine.UpdateMany(collection, extend, predicate);
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public int Upsert(string collection, IEnumerable<BsonDocument> docs, BsonAutoId autoId)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
int val;
try
{
val = _engine.Upsert(collection, docs, autoId);
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public int Delete(string collection, IEnumerable<BsonValue> ids)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
int val;
try
{
val = _engine.Delete(collection, ids);
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public int DeleteMany(string collection, BsonExpression predicate)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
int val;
try
{
val = _engine.DeleteMany(collection, predicate);
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public bool DropCollection(string name)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
bool val = false;
try
{
val = _engine.DropCollection(name);
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public bool RenameCollection(string name, string newName)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
bool val = false;
try
{
val = _engine.RenameCollection(name, newName);
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public bool DropIndex(string collection, string name)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
bool val = false;
try
{
val = _engine.DropIndex(collection, name);
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
public bool EnsureIndex(string collection, string name, BsonExpression expression, bool unique)
{
EnsureLocking();
lock (_mutex)
{
isMutexLocking = true;
try
{
_mutex.WaitOne();
}
catch (AbandonedMutexException) { }
catch { isMutexLocking = false; throw; }
this.OpenDatabase();
bool val = false;
try
{
val = _engine.EnsureIndex(collection, name, expression, unique);
}
catch
{
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
throw;
}
this.CloseDatabase();
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
return val;
}
}
#endregion
private void EnsureLocking()
{
int count = 0;
System.Threading.Tasks.Task delay;
while (isMutexLocking)
{
delay = System.Threading.Tasks.Task.Delay(
TimeSpan.FromMilliseconds(
rand.Next(3, 7)
)
);
delay.Wait();
if (!isMutexLocking) { break; }
if (delay.IsCompleted)
{
++count;
if (count > 100)
{
count = 0;
throw new TimeoutException("Shared threading controller with Mutex is Locking.");
}
}
}
}
public void Dispose()
{
this.Dispose(false);
}
public void Dispose(string fromQuery = "yes")
{
this.Dispose(true);
isMutexLocking = false;
GC.SuppressFinalize(this);
}
~SharedEngine()
{
this.Dispose(false);
try { _mutex.ReleaseMutex(); } catch { isMutexLocking = false; throw; }
isMutexLocking = false;
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_engine == null || isDisposed)
{
isDisposed = true;
return;
}
if (disposing)
{
if (_engine != null)
{
if (!isMutexLocking)
{
lock (_mutex)
{
isMutexLocking = true;
try
{
_engine.Dispose();
_engine = null;
}
catch
{
_engine = null;
}
finally
{
isDisposed = true;
}
isMutexLocking = false;
}
}
else
{
try
{
_engine.Dispose();
_engine = null;
}
catch
{
_engine = null;
}
finally
{
isDisposed = true;
}
}
}
}
GC.Collect();
}
}
} What it needs is using a retry: /* codes for using Shared Engine LiteDB database and get the collection */
var collection = ... ;
/* using a retry , start from here */
bool isSuccessful = true;
int retryCount = 0;
while(true){
try
{
isSuccessful = isSuccessful && collection.Upsert<T>(entity);
if (isSuccessful) { break; }
}
catch
{
++retryCount;
/** ... code for doing log for retry count, catched exception, and others ... **/
if (retryCount > 10) { throw; }
}
} I am not getting Exception from throw, and it log retry count less then 2 times with my project code. |
@nightroman @MidasLamb @lbnascimento My solution: force all mutex operations to run on the same thread. This test (#1546 (comment)) is passed. Note: I forgot the Dispose() method Thanks |
I got the same error, too. |
Same problem here after switching to a shared connection with LiteDb 5, had to ditch LiteDB and switch to our MongoDB layer because this broke our application |
Any news on this? |
Following, as this is an issue for us as well. Any ETA? |
This problem still happen on version 5.0.11. |
Still not fixed in 5.0.11. Any update or ETA on the fix? |
Encountered the same issue |
I just stepped on the same error when I was insertingdata wtthin a public void Foo(IEnumerable<string> pathsToSearch)
{
// Do work
// Throws exception in parallel
// Parallel.ForEach(pathsToSearch, path => Bar(path));
// Good old foreach
foreach (var path in pathsToSearch)
{
Bar(path)
}
}
public static void Bar(string path)
{
// Do work
// create T entity
collection.Insert(entity);
} |
Does it fixed in 5.0.12? |
@a44281071 no this isn't fixed in 5.0.12. Obviously multi-threading is complicated to get right. Unfortunately the readme file claims LiteDb is thread-safe, when it really isn't fully (yet...). |
I don't know what to do now as our product set set to launch and almost completed. Any update on this friends? If no then I would prefer to go for SQLServer or SQLite. Very disappointed :-( to find this issue at this stage anyways. |
Okay, I've found why it's giving me thread-lock issues intermittently. To my understanding this is causing because of the proper wentout of dispose from multiple threads. Here's how I fixed (atleast for me)
Since I made repositories Scoped, The dependent service/business layer also need to be changed to scoped. Anyway this fixed my issue for now. |
same problem |
We are getting the same problems while integration testing APIs with Changing all our access types to use Transient/Singleton would be too much work =/ |
I am trying to wrap my head around the mutex usage. Since it's a global mutex, it handles multiprocess access properly. When it comes to single-process multithreaded scenario, it fails. Would it be possible to add a check at the beginning of the OpenDatabase method, to check the caller? If it's a new process or a new thread of a current processes, the scenario matters. With this runtime check, all processes has to use two mutexes: a global one for all processes, and a local mutex per each process (or the first thread of each process). It also affects releasing too. Yet it may work. Also, instead of a local mutex, a semaphore might work too. But these are just naive assumptions. I need to test. |
It look like this problem is resolved as I tried to test the |
I'm getting this error intermittently while running with ConnectionType.Shared and don't with the default.
I tried researching this but haven't come up with any ideas.
Does anyone have any suggestions on why I'm getting this?
The text was updated successfully, but these errors were encountered: