1

I have a .net DLL which calls a c++ DLL which is not thread safe. The .NET DLL is run in IIS. IIS application pool loads a single copy of both DLLs and when two browsers sessions calls the .NET DLL at the same time the c++ DLL Ralph's on its shoes. In certain cases it will cause the application pool to fail in others I get a trappable error.

I attempted to use the Monitor Class to solve my problem like;

    Private objSync As Object = New Object
.
.
.
.
        Monitor.Enter(objSync)
        editsErrorHandler(MycPlusPlusCall(_intsmfID, _strEditSetTag, _strEditLayoutTag, strNAACCRRecord,
                                            EE_SKIPFAIL, intErrCount, _ptrThis, _ptrEditsMessagerHandler),
                                           "MycPlusPlusCall")
        objMessageTransport.intMstErrorCount = intErrCount
        objMessageTransport.objMstMessages = _objErrorMessages
        '        Debug.Print("intErrCount=" & intErrCount)
        Monitor.Exit(objSync)

MycPlusPlusCall is the c++ DLL call.

This seemed to have no impact.

This is my first foray into calling non thread safe DLLs.

How can I force multiple calls from the application pool to wait until the previous call finishes?

Edit:

I moved the Monitor up one level and have succeeded in getting W3WP to crash almost every time. When it crashes I get AppCrash on w3wp with the module being the c++ DLL.

The .NET DLL referenced above is called by a DLL "above" it so I moved the Monitor.Enter & Exit to that level. The new & improved code looks like;

    Private Function processRecord(ByVal strInterRecordEditsName As String, _
                                   ByVal objConnection As OracleConnection) As Integer
        Dim strNAACCRRecord As String = buildNAACCRRecord(_objRecord)
        '           Dim strNAACCRRecord As String = _objRecord("NAACCR_RECORD")
        Dim objErrorMessageTransport As ACEEdits50.EditMessageTransport
        Dim intErrCount As Integer = 0
        Dim objErrorMessages As List(Of ACEEdits50.EditMessage) = New List(Of ACEEdits50.EditMessage)
        Dim strDebug As String = "Section 10"
        On Error GoTo processRecordError
        Monitor.Enter(_objACEEdits)
        ' Here is where the heavy lifting is done.
        objErrorMessageTransport = _objACEEdits.runEdits(strNAACCRRecord & Space(_intNAACCRRecordSize - strNAACCRRecord.Length))
        Monitor.Exit(_objACEEdits)

Edit 2:

So I removed the higher "Monitor.Enter/Exit" and replaced it with SyncLock as recommended below, with the added option of making the object "private shared" So the code looks like;

Public Class ACEEdits50
    Public Sub New()

    End Sub
.
.
.
.
    Private Shared objSync As Object = New Object

.
.
.
.
    Public Function runEdits(ByVal strNAACCRRecord As String) As EditMessageTransport
.
.
.
.
        SyncLock objSync
            editsErrorHandler(Edit_RunEdits(_intsmfID, _strEditSetTag, _strEditLayoutTag, strNAACCRRecord,
                                                EE_SKIPFAIL, intErrCount, _ptrThis, _ptrEditsMessagerHandler),
                                               "Edit_RunEdits")
            objMessageTransport.intMstErrorCount = intErrCount
            objMessageTransport.objMstMessages = _objErrorMessages
            '        Debug.Print("intErrCount=" & intErrCount)
        End SyncLock

I still get the same appCrash.

Paul Stearns
  • 856
  • 9
  • 30
  • What about using mutexes? – πάντα ῥεῖ Aug 05 '19 at 18:20
  • 1
    vb.net has the `SyncLock` – Daniel A. White Aug 05 '19 at 18:23
  • Are you sure this isn't working? Other than putting the code in a try/finally block (so that the Monitor.Exit is called in all cases), this is the standard pattern. As @DanielA.White points out, the language-provided `SyncLock` keyword (or `lock` in C#) will do this for you. – Flydog57 Aug 05 '19 at 18:25
  • 1
    you should also use the same reference to lock on – Daniel A. White Aug 05 '19 at 18:27
  • Does the objSync need to be marked as static? It's hard to tell if this is inside an instance class, but if it is each instance might have an objSync. – Cory Aug 05 '19 at 19:02
  • Is it not working is an interesting question. I am not getting the desired results, that's for sure. When I call the .NET DLL from two different web sessions, it still dies . – Paul Stearns Aug 05 '19 at 19:42
  • Isn't the problem that the two application pools each have their own memory space and references to the dll, so even a static object in the dll is duplicated in each reference? – djv Aug 05 '19 at 20:54

2 Answers2

0

Try it this way

Private Shared protectIt As New Threading.AutoResetEvent(True)
Private Sub TestSimul()
    protectIt.WaitOne() 'block forever
    '
    'code being protected
    ' when done
    protectIt.Set()
End Sub
dbasnett
  • 11,334
  • 2
  • 25
  • 33
0

You can use a named Mutex which is used to synchronize between processes in the same OS

Public Class ACEEdits50

    Public Function runEdits(ByVal strNAACCRRecord As String) As EditMessageTransport
        Using m = New Threading.Mutex(False, "ACEEdits50")
            While Not m.WaitOne(20000) ' wait 20 seconds for mutex
                Console.WriteLine("Waiting for mutex")
            End While
            editsErrorHandler(Edit_RunEdits(_intsmfID, _strEditSetTag, _strEditLayoutTag, strNAACCRRecord, EE_SKIPFAIL, intErrCount, _ptrThis, _ptrEditsMessagerHandler), "Edit_RunEdits")
            objMessageTransport.intMstErrorCount = intErrCount
            objMessageTransport.objMstMessages = _objErrorMessages
            m.ReleaseMutex()
        End Using

    End Function

End Class

See this answer for more reference in C#

djv
  • 15,168
  • 7
  • 48
  • 72
  • I did use mutexes, not exactly your code, but it seems to work. Some of the other solutions may also have worked as well. The reason I say that is I was told some calls into the DLL were thread safe, and others weren't. I was only protect those I was told were problematic. I ended up wrapping all calls to the DLL in a mutexes and it worked. – Paul Stearns Aug 05 '19 at 22:31