ReaderWriterLock blocks when trying to read resource - .net-core

I am trying to use a ReaderWriterLock for a shared resource between two Tasks.For some reason it starts waiting indefinitely:
class State {
private const int TIMEOUT = 5000;
private ReaderWriterLock lck = new ReaderWriterLock();
private TimeSpan lastIssuedAt;
public TimeSpan LastIssuedAt {
get {
this.lck.AcquireReaderLock(TIMEOUT);
return this.lastIssuedAt;
}
set {
this.lck.AcquireWriterLock(TIMEOUT);
this.lastIssuedAt = value;
}
}
}
When a task tries to get the property LastIssuedAt it just blocks and I do not understand why.

Take a look at the example on MSDN: https://learn.microsoft.com/en-us/dotnet/api/system.threading.readerwriterlock?view=netframework-4.8
You forgot to release the lock. A common pattern for doing so is try/finally:
ReaderWriterLock lck = new ReaderWriterLock();
lck.AcquireReaderLock(timeOut);
try
{
// Do what needs to be done under the lock
}
finally
{
// Ensure that the lock is released.
lck.ReleaseReaderLock();
}
Also, check out ReaderWriterLockSlim: https://learn.microsoft.com/en-us/dotnet/api/system.threading.readerwriterlockslim?view=netframework-4.8
Which MSDN recommends for new development.

Related

ASP.NET, thread and connection-string stored in Session

My application can connect with multiple data bases (every data base have the same schema), I store the current DB, selected by user, in Session and encapsule access using a static property like:
public class DataBase
{
public static string CurrentDB
{
get
{
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
HttpContext.Current.Session["CurrentDB"] = value;
}
}
}
Other pieces of code access the static CurrentDB to determine what DB use.
Some actions start background process in a thread and it need access the CurrentDB to do some stuff. I'm thinking using something like this:
[ThreadStatic]
private static string _threadSafeCurrentDB;
public static string CurrentDB
{
get
{
if (HttpContext.Current == null)
return _threadSafeCurrentDB;
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
if (HttpContext.Current == null)
_threadSafeCurrentDB = value;
else
HttpContext.Current.Session["CurrentDB"] = value;
}
}
And start thread like:
public class MyThread
{
private string _currentDB;
private thread _thread;
public MyThread (string currentDB)
{
_currentDB = currentDB;
_thread = new Thread(DoWork);
}
public DoWork ()
{
DataBase.CurrentDB = _currentDB;
... //Do the work
}
}
This is a bad practice?
Actually, I think you should be able to determine which thread uses which database, so I would create a class inherited from Thread, but aware of the database it uses. It should have a getDB() method, so, if you need a new Thread which will use the same database as used in another specific Thread, you can use it. You should be able to setDB(db) of a Thread as well.
In the session you are using a current DB approach, which assumes that there is a single current DB. If this assumption describes the truth, then you can leave it as it is and update it whenever a new current DB is being used. If you have to use several databases in the same time, then you might want to have a Dictionary of databases, where the Value would be the DB and the Key would be some kind of code which would have a sematic meaning which you could use to be able to determine which instance is needed where.

Synchronous responses to `Gdx.net.sendHttpRequest` in LibGDX

I'm making a small game in LibGDX. I'm saving the player's username locally as well as on a server. The problem is that the application is not waiting for the result of the call so the online database's ID is not saved locally. Here's the overall flow of the code:
//Create a new user object
User user = new User(name);
//Store the user in the online database
NetworkService networkService = new NetworkService();
String id = networkService.saveUser(user);
//Set the newly generated dbase ID on the local object
user.setId(id);
//Store the user locally
game.getUserService().persist(user);
in this code, the id variable is not getting set because the saveUser function is returning immediately. How can I make the application wait for the result of the network request so I can work with results from the server communication?
This is the code for saveUser:
public String saveUser(User user) {
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("action", "save_user");
parameters.put("json", user.toJSON());
HttpRequest httpGet = new HttpRequest(HttpMethods.POST);
httpGet.setUrl("http://localhost:8080/provisioner");
httpGet.setContent(HttpParametersUtils.convertHttpParameters(parameters));
WerewolfsResponseListener responseListener = new WerewolfsResponseListener();
Gdx.net.sendHttpRequest (httpGet, responseListener);
return responseListener.getLastResponse();
}
This is the WerewolfsResponseListener class:
class WerewolfsResponseListener implements HttpResponseListener {
private String lastResponse = "";
public void handleHttpResponse(HttpResponse httpResponse) {
System.out.println(httpResponse.getResultAsString());
this.lastResponse = httpResponse.getResultAsString();
}
public void failed(Throwable t) {
System.out.println("Saving user failed: "+t.getMessage());
this.lastResponse = null;
}
public String getLastResponse() {
return lastResponse;
}
}
The asynchrony you are seeing is from Gdx.net.sendHttpRequest. The methods on the second parameter (your WerewolfsResponseListener) will be invoked whenever the request comes back. The success/failure methods will not be invoked "inline".
There are two basic approaches for dealing with callbacks structured like this: "polling" or "events".
With polling, your main game loop could "check" the responseListener to see if its succeeded or failed. (You would need to modify your current listener a bit to disambiguate the success case and the empty string.) Once you see a valid response, you can then do the user.setId() and such.
With "events" then you can just put the user.setId() call inside the responseListener callback, so it will be executed whenever the network responds. This is a bit more of a natural fit to the Libgdx net API. (It does mean your response listener will need a reference to the user object.)
It is not possible to "wait" inline for the network call to return. The Libgdx network API (correctly) assumes you do not want to block indefinitely in your render thread, so its not structured for that (the listener will be queued up as a Runnable, so the earliest it can run is on the next render call).
I would not recommend this to any human being, but if you need to test something in a quick and dirty fashion and absolutely must block, this will work. There's no timeout, so again, be prepared for absolute filth:
long wait = 10;
while(!listener.isDone())
{
Gdx.app.log("Net", "Waiting for response");
try
{
Thread.sleep(wait *= 2);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public static class BlockingResponseListener implements HttpResponseListener
{
private String data;
private boolean done = false;
private boolean succeeded = false;
#Override
public void handleHttpResponse(HttpResponse httpResponse)
{
Gdx.app.log("Net", "response code was "+httpResponse.getStatus().getStatusCode());
data = httpResponse.getResultAsString();
succeeded = true;
done = true;
}
#Override
public void failed(Throwable t)
{
done = true;
succeeded = false;
Gdx.app.log("Net", "Failed due to exception ["+t.getMessage()+"]");
}
public boolean succeeded()
{
return succeeded;
}
public boolean isDone()
{
return done;
}
public String getData()
{
return data;
}
}

nhibernate session manager implementation

I am new to Nhibernate and slowing working my way thru learning it. I tried to implement a session manager class to help me get the session for my db calls. Below is the code for it. Can someone please say if this is architecturally correct and foresee any issue of scalability or performance?
public static class StaticSessionManager
{
private static ISession _session;
public static ISession GetCurrentSession()
{
if (_session == null)
OpenSession();
return _session;
}
private static void OpenSession()
{
_session = (new Configuration()).Configure().BuildSessionFactory().OpenSession();
}
public static void CloseSession()
{
if (_session != null)
{
_session.Close();
_session = null;
}
}
}
and in my data provider class, I use the following code to get data.
public class GenericDataProvider<T>
{
NHibernate.ISession _session;
public GenericDataProvider()
{
this._session = StaticSessionManager.GetCurrentSession();
}
public T GetById(object id)
{
using (ITransaction tx = _session.BeginTransaction())
{
try
{
T obj = _session.Get<T>(id);
tx.Commit();
return obj;
}
catch (Exception ex)
{
tx.Rollback();
StaticSessionManager.CloseSession();
throw ex;
}
}
}
}
and then
public class UserDataProvider : GenericDataProvider<User>
{
public User GetUserById(Guid uid)
{
return GetById(uid)
}
}
Final usage in Page
UserDataProvider udp = new UserDataProvider();
User u = udp.GetUserById(xxxxxx-xxx-xxx);
Is this something that is correct? Will instantiating lot of data providers in a single page cause issues?
I am also facing an issue right now, where if I do a same read operation from multiple machines at the same time, Nhibernate throws random errors- which I think is due to transactions.
Please advice.
From what I can see you are building the session factory if you have a null session. You should only call BuildSessionFactory() once when the application starts.
Where you do this is up to you, some people build the SessionFactory inside Global.asax in the method application_start or in your case have a static property for sessionFactory instead of session in your StaticSessionManager class.
I suspect your errors are due to the fact that your session factory is being built multiple times!
Another point is that some people open a transaction _session.BeginTransaction() at the beginning of each request and either commit or rollback at the end of each request. This gives you a unit of work which means you can lose the
using (ITransaction tx = _session.BeginTransaction())
{
...
}
on every method. All of this is open for debate but I use this method for 99% of all my code with no trouble at all.

nHibernate logging with Log4Net, thread session issue

Hey there folks, having a little issue here which I'm trying to wrap my head around.
I'm currently starting out with nHibernate, such I have to due to work requirements, and am getting a little stuck with nHibernate's Sessions and multiple threads. Well the task I want to complete here is to have Log4Net log everything to the database, including nHibernate's debug/errors etc.
So what I did was create a very simple Log4Net:AppenderSkeleton class which fires perfectly when I need it. My intial issue I ran into was that when I used GetCurrentSession, obviously since Log4Net runs on a seperate thread(s), it errored out with the initial thread's session. So I figured that I had to create a new nHiberante Session for the Log4Net AppenderSkeleton class. The code is below:
public class Custom : AppenderSkeleton
{
protected override void Append(LoggingEvent loggingEvent)
{
if (loggingEvent != null)
{
using (ISession session = NHibernateHelper.OpenSession())
{
using (ITransaction tran = session.BeginTransaction())
{
Log data = new Log
{
Date = loggingEvent.TimeStamp,
Level = loggingEvent.Level.ToString(),
Logger = loggingEvent.LoggerName,
Thread = loggingEvent.ThreadName,
Message = loggingEvent.MessageObject.ToString()
};
if (loggingEvent.ExceptionObject != null)
{
data.Exception = loggingEvent.ExceptionObject.ToString();
}
session.Save(data);
tran.Commit();
}
}
}
}
Simple enough idea really, while it is at its basic form now, I will have more error checking info etc but for now the issue is that while this works perfectly it creates multiple sessions. That is, it creates a new session per error logged since I can't use GetCurrentSession as this will get the calling Session (the main program flow). I'm sure there is a way for me to create a session globally for Log4Net's thread, but I'm unsure gow to. Keeping in mind that I already bind a Session to the intial thread using the below in Global.asax (Application_BeginRequest):
ISession session = NHibernateHelper.OpenSession();
CurrentSessionContext.Bind(session);
And for those that will ask, the contents of my helper is below (this is in a DLL):
public static class NHibernateHelper
{
private static Configuration _nHibernateConfig;
private static ISessionFactory _nHibernateSessionFactory;
private static ISessionFactory BuildNHibernateSessionFactory
{
get
{
if (_nHibernateSessionFactory == null)
{
if (_nHibernateConfig == null)
{
BuildSessionFactory();
}
_nHibernateSessionFactory = _nHibernateConfig.BuildSessionFactory();
}
return _nHibernateSessionFactory;
}
}
private static Configuration BuildNHibernateConfig
{
get
{
if (_nHibernateConfig == null)
{
_nHibernateConfig = new ConfigurationBuilder().Build();
}
return _nHibernateConfig;
}
}
public static Configuration nHibernateConfig
{
get
{
return _nHibernateConfig;
}
}
public static ISessionFactory nHibernateSessionFactory
{
get
{
return _nHibernateSessionFactory;
}
}
public static Configuration BuildConfiguration()
{
return BuildNHibernateConfig;
}
public static ISessionFactory BuildSessionFactory()
{
return BuildNHibernateSessionFactory;
}
public static ISession OpenSession()
{
return _nHibernateSessionFactory.OpenSession();
}
public static ISession GetCurrentSession()
{
try
{
return _nHibernateSessionFactory.GetCurrentSession();
}
catch (HibernateException ex)
{
if(ex.Message == "No session bound to the current context")
{
// See if we can bind a session before complete failure
return _nHibernateSessionFactory.OpenSession();
}
else
{
throw;
}
}
catch (Exception ex)
{
throw;
}
}
}
I realise I could use the ADO appender in log4net but I wish to use nHibernate directly to add the data to the database. The reason being is I don't wish to mess around with connectionstrings etc when nHibernate is already working.
As always, any help is always appreciated.
-- Edit: --
So based on what I have been told initialy, I modified my custom Log4Net logger code. There are two versions below. My question, which is best or is there a better way?
The first, according to nHibernate Prof, creates only two sessions - The intial session is for the main program flow as intended and the second for my Log4Net logger code. Yet this has hundreds of enteries in the second session and complains about too many enteries and to many calls to the database.
The second, nHibernate prof shows many sessions, as many sessions as there are calls to the logger +1 for the main program flow. Yet no complaints anywhere on nHprof. Though I have this feeling that having that many sessions would have people frowning or is too much tasking.
Anyway the codes:
Code 1 -
protected override void Append(LoggingEvent loggingEvent)
{
if (!System.Web.HttpContext.Current.Items.Contains("Log4Net nHibernate Session"))
{
System.Web.HttpContext.Current.Items.Add("Log4Net nHibernate Session", NHibernateHelper.OpenStatelessSession());
}
IStatelessSession statelessSession = System.Web.HttpContext.Current.Items["Log4Net nHibernate Session"] as IStatelessSession;
if (statelessSession != null && loggingEvent != null)
{
using (ITransaction tran = statelessSession.BeginTransaction())
{
Log data = new Log
{
Id = Guid.NewGuid(),
Date = loggingEvent.TimeStamp,
Level = loggingEvent.Level.ToString(),
Logger = loggingEvent.LoggerName,
Thread = loggingEvent.ThreadName,
Message = loggingEvent.MessageObject.ToString()
};
if (loggingEvent.ExceptionObject != null)
{
data.Exception = loggingEvent.ExceptionObject.ToString();
}
statelessSession.Insert(data);
tran.Commit();
}
}
}
Code 2 -
protected override void Append(LoggingEvent loggingEvent)
{
if (loggingEvent != null)
{
using (IStatelessSession statelessSession = NHibernateHelper.OpenStatelessSession())
using (ITransaction tran = statelessSession.BeginTransaction())
{
Log data = new Log
{
Id = Guid.NewGuid(),
Date = loggingEvent.TimeStamp,
Level = loggingEvent.Level.ToString(),
Logger = loggingEvent.LoggerName,
Thread = loggingEvent.ThreadName,
Message = loggingEvent.MessageObject.ToString()
};
if (loggingEvent.ExceptionObject != null)
{
data.Exception = loggingEvent.ExceptionObject.ToString();
}
statelessSession.Insert(data);
tran.Commit();
}
}
}
You are right about creating a new session. You definitely don't want to share the same session across threads. In your logging instance I would even say to use an IStatelessSession. Also sessions should be fairly lightweight so I wouldn't worry about creating new sessions each time you log a statement.
NHibernate already uses Log4Net internally so you just need to enable the logger and use an AdoNetAppender to send the logs to your database.
<log4net>
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
...
</appender>
<logger name="NHibernate">
<level value="WARN"/>
<appender-ref ref="AdoNetAppender"/>
</logger>
</log4net>

Is this ok for using EntityFramework context

In my DAL, I'm currently using this in a base class:
protected static MyCMSEntities MyCMSDb
{
get { return new MyCMSEntities(ConfigurationManager.ConnectionStrings["MyCMSEntities"].ConnectionString); }
}
and calling like this from a subclass:
public static bool Add(ContentFAQ newContent)
{
MyCMSEntities db = MyCMSDb;
newContent.DateModified = DateTime.Now;
newContent.OwnerUserId = LoginManager.CurrentUser.Id;
db.ContentFAQ.AddObject(newContent);
return db.SaveChanges() > 0;
}
I understand the method to get the context is static, but as it creates a new intance of the context, this is not static, i.e. it is new for each call to the Add method.
Am I correct and more importantly, ok for a web application?
Thanks.
You are correct in using a new context for every web call - but why this obfuscation? I would recommend removing this indirection with the static property (makes the code harder to understand) and also using a using block since the context is disposable:
public static bool Add(ContentFAQ newContent)
{
using(var db = new MyCMSEntities())
{
newContent.DateModified = DateTime.Now;
newContent.OwnerUserId = LoginManager.CurrentUser.Id;
db.ContentFAQ.AddObject(newContent);
return db.SaveChanges() > 0;
}
}
Also the default constructor of the context should use the default connection string, which is the right one if you didn't change it in your configuration (otherwise just add it back in).

Resources