Skip to content

Commit

Permalink
Merge pull request #61 from glenebob/async_notification
Browse files Browse the repository at this point in the history
Async notification fixes

This will fix the CPU hog problem you were seeing, and eliminates the need for the odd Sleep() call in ProcessServerMessages().

-Glen
  • Loading branch information
franciscojunior committed Oct 2, 2013
2 parents 1784558 + b4169d7 commit 9a88a14
Showing 1 changed file with 29 additions and 28 deletions.
57 changes: 29 additions & 28 deletions src/Npgsql/NpgsqlConnector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,6 @@ internal class NpgsqlConnector

private Thread _notificationThread;

// The AutoResetEvent to synchronize processing threads.
internal AutoResetEvent _notificationAutoResetEvent;

// Counter of notification thread start/stop requests in order to
internal Int16 _notificationThreadStopCount;

Expand Down Expand Up @@ -197,7 +194,6 @@ public NpgsqlConnector(NpgsqlConnectionStringBuilder ConnectionString, bool Pool
_planIndex = 0;
_portalIndex = 0;
_notificationThreadStopCount = 1;
_notificationAutoResetEvent = new AutoResetEvent(true);
}

//Finalizer should never be used, but if some incident has left to a connector being abandoned (most likely
Expand Down Expand Up @@ -961,21 +957,21 @@ internal String NextPlanName()
internal void RemoveNotificationThread()
{
// Wait notification thread finish its work.
_notificationAutoResetEvent.WaitOne();

// Kill notification thread.
_notificationThread.Abort();
_notificationThread = null;
lock (_socket)
{
// Kill notification thread.
_notificationThread.Abort();
_notificationThread = null;

// Special case in order to not get problems with thread synchronization.
// It will be turned to 0 when synch thread is created.
_notificationThreadStopCount = 1;
// Special case in order to not get problems with thread synchronization.
// It will be turned to 0 when synch thread is created.
_notificationThreadStopCount = 1;
}
}

internal void AddNotificationThread()
{
_notificationThreadStopCount = 0;
_notificationAutoResetEvent.Set();

NpgsqlContextHolder contextHolder = new NpgsqlContextHolder(this, CurrentState);

Expand Down Expand Up @@ -1025,24 +1021,26 @@ private void StopNotificationThread()
// first check to see if an exception has
// been thrown by the notification thread.
if (_notificationException != null)
{
throw _notificationException;
}

_notificationThreadStopCount++;

if (_notificationThreadStopCount == 1) // If this call was the first to increment.
{
_notificationAutoResetEvent.WaitOne();
Monitor.Enter(_socket);
}
}

private void ResumeNotificationThread()
{
_notificationThreadStopCount--;

if (_notificationThreadStopCount == 0)
{
// Release the synchronization handle.

_notificationAutoResetEvent.Set();
Monitor.Exit(_socket);
}
}

Expand All @@ -1068,28 +1066,31 @@ internal void ProcessServerMessages()
{
while (true)
{
Thread.Sleep(0);
//To give runtime chance to release correctly the lock. See http://pgfoundry.org/forum/message.php?msg_id=1002650 for more information.
this.connector._notificationAutoResetEvent.WaitOne();

if (this.connector.Socket.Poll(100, SelectMode.SelectRead))
// Mono's implementation of System.Threading.Monitor does not appear to give threads
// priority on a first come/first serve basis, as does Microsoft's. As a result,
// under mono, this loop may execute many times even after another thread has attempted
// to lock on _socket. A short Sleep() seems to solve the problem effectively.
// Note that Sleep(0) does not work.
Thread.Sleep(1);

lock (connector._socket)
{
// reset any responses just before getting new ones
this.connector.Mediator.ResetResponses();
this.state.ProcessBackendResponses(this.connector);
// 20 millisecond timeout
if (this.connector.Socket.Poll(20000, SelectMode.SelectRead))
{
// reset any responses just before getting new ones
this.connector.Mediator.ResetResponses();
this.state.ProcessBackendResponses(this.connector);
}
}

this.connector._notificationAutoResetEvent.Set();
}
}
catch (IOException ex)
{
this.connector._notificationException = ex;
this.connector._notificationAutoResetEvent.Set();
}

}

}

public bool RequireReadyForQuery
Expand Down

0 comments on commit 9a88a14

Please sign in to comment.