Windows based shared memory using events

Share your advanced PureBasic knowledge/code with the community.
User avatar
RichAlgeni
Addict
Addict
Posts: 935
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Windows based shared memory using events

Post by RichAlgeni »

I was working on an issue with pipelining multiple updates into a single process in regards to a database product, when I started thinking about sharing memory with multiple processes, and using events to control access. Here are two processes, one a producer, and one a consumer. I've used to Windows events to keep one process from stepping on the other. No real use for it, just an interesting concept.

The producer process must be started first. It created the shared memory area based upon a name, and two events. It also writes an initial string to the shared memory, then sets a read event, then waits on a send event trigger. The consumer process reads in the memory, sets a write event, then waits on a read event trigger.

Code: Select all

; shm_producer.pb

EnableExplicit

Define version.s   = "1.0.01e"
Define hMap.i
Define result.i
Define mapName.s   = "SharedMapName"
Define readEventNumber.i
Define sendEventNumber.i
Define sendStr.s   = ""
Define mapSize.i   = 16384
Define stringLen.i

readEventNumber = CreateEvent_(0, 0, 0, "read_" + mapName + "_event")
sendEventNumber = CreateEvent_(0, 0, 0, "send_" + mapName + "_event")

; procedure to write data to the shared memory map

Procedure.i WriteMapString(*mapName, *sendMemory, stringLen)

    Protected hFileMap.i
    Protected *mapView
    Protected retValue.i = #False

    hFileMap = OpenFileMapping_(#FILE_MAP_WRITE, 0, *mapName)
    If hFileMap
        *mapView = MapViewOfFile_(hFileMap, #FILE_MAP_WRITE, 0, 0, 0)

        If *mapView
            CopyMemory(*sendMemory, *mapView, stringLen)
            PokeA(*mapView + stringLen, 0)
            retValue = #True

            UnmapViewOfFile_(*mapView)
        Else
            retValue = -2
        EndIf
        CloseHandle_(hFileMap)
    Else
        retValue = -1
    EndIf

    ProcedureReturn retValue

EndProcedure

; use the #INVALID_HANDLE_VALUE to tell the system to use memory, not a physical file!

hMap = CreateFileMapping_(#INVALID_HANDLE_VALUE, #Null, #PAGE_READWRITE, 0, mapSize + 1, @mapName)

OpenConsole("Shared Map Producer, version: " + version)

sendStr   = "Started Shared Map Producer"
stringLen = Len(sendStr)
result    = WriteMapString(@mapName, @sendStr, stringLen)
If result > 0
    PrintN("Send to shared Map, initial value sent is: " + sendStr + ", packet sent ok")
Else
    PrintN("Error writing data to shared map, process will terminate, result = " + Str(result))
    Input()
    End
EndIf

Repeat
    Print("Enter text To send, or press <enter> to quit: ")
    sendStr   = Input()
    stringLen = Len(sendStr)

    If stringLen > 0
        If  stringLen > mapSize
            stringLen = mapSize
            sendStr   = LSet(sendStr, stringLen)
        EndIf

        result = WriteMapString(@mapName, @sendStr, stringLen)

        SetEvent_(readEventNumber)
        WaitForSingleObject_(sendEventNumber, #INFINITE)
    Else
        SetEvent_(readEventNumber)
        Break
    EndIf
ForEver

CloseHandle_(hMap)

CloseConsole()

End
; IDE Options = PureBasic 5.42 LTS (Windows - x64)
; CursorPosition = 17
; FirstLine = 3
; Executable = shm_producer.exe
; DisableDebugger
; HideErrorLog
; CompileSourceDirectory
; EnableCompileCount = 9
; EnableBuildCount = 9
; EnableExeConstant

Code: Select all

; shm_consumer.pb

EnableExplicit

Define version.s = "1.0.01e"
Define lenSent.i
Define sentText.s
Define mapName.s   = "SharedMapName"
Define readEventNumber.i
Define sendEventNumber.i
Define eventNumber.i
Define firstTime.i = #True
Define *recvString = AllocateMemory(16384)

readEventNumber = CreateEvent_(0, 0, 0, "read_" + mapName + "_event")
sendEventNumber = CreateEvent_(0, 0, 0, "send_" + mapName + "_event")

; procedure to open he shared memory map, and read the data

Procedure.i GetMapPointer(*mapName, *recvMemory)

    Protected lenSent.i = 0
    Protected *mapView
    Protected hFileMap.i
    Protected sentText.s

    hFileMap = OpenFileMapping_(#FILE_MAP_READ, 0, *mapName)

    If hFileMap
        *mapView = MapViewOfFile_(hFileMap, #FILE_MAP_READ, 0, 0, 0)
        If *mapView
            sentText = PeekS(*mapView)
            lenSent  = Len(sentText)
            UnmapViewOfFile_(*mapView)
        EndIf

        CloseHandle_(hFileMap)

        If  lenSent
            lenSent = PokeS(*recvMemory, sentText, lenSent)
        EndIf
    EndIf

    ProcedureReturn lenSent

EndProcedure

OpenConsole("Shared Map Consumer, version: " + version)

Repeat
    lenSent = GetMapPointer(@mapName, *recvString)
    If lenSent > 0
        sentText = PeekS(*recvString, lenSent)
        PrintN("Read Map = " + Str(lenSent) + " bytes received: " + sentText)

        If  firstTime
            firstTime = #False
        EndIf

        SetEvent_(sendEventNumber)
        WaitForSingleObject_(readEventNumber, #INFINITE)
    Else
        Break
    EndIf
ForEver

If firstTime
    PrintN("The shared memory producer must be started first!")
    Input()
EndIf

CloseConsole()

End
; IDE Options = PureBasic 5.42 LTS (Windows - x64)
; CursorPosition = 18
; FirstLine = 3
; Executable = shm_consumer.exe
; DisableDebugger
; HideErrorLog
; CompileSourceDirectory
; EnableCompileCount = 11
; EnableBuildCount = 11
; EnableExeConstant
User avatar
JHPJHP
Addict
Addict
Posts: 2258
Joined: Sat Oct 09, 2010 3:47 am

Re: Windows based shared memory using events

Post by JHPJHP »

Hi RichAlgeni,

I did something similar in Services, Stuff, and Shellhook.
- Stuff\CopyShare\ShareMemory
-- \Image\ and \Text\

\Image\
- opening an image in one window will be duplicated in another window

\Text\
- entering text in one window can be "sent" to another window
Last edited by JHPJHP on Thu May 12, 2016 3:54 am, edited 1 time in total.

If you're not investing in yourself, you're falling behind.

My PureBasic StuffFREE STUFF, Scripts & Programs.
My PureBasic Forum ➤ Questions, Requests & Comments.
User avatar
RichAlgeni
Addict
Addict
Posts: 935
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Windows based shared memory using events

Post by RichAlgeni »

JHPJHP wrote: I did something similar in [url=http://www.purebasic.fr/english/viewtop ... 12&t=60881]Services, Stuff, and Shellhook.
Very good! When I get some time, I'll look into adding slim locks so that it can trusted in a production environment.
User avatar
RichAlgeni
Addict
Addict
Posts: 935
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Windows based shared memory using events

Post by RichAlgeni »

New and improved, with Mutexes! Or is it Muti???

Code: Select all

; shm_producer.pb

EnableExplicit

Define version.s   = "1.0.02b"
Define hMap.i
Define result.i
Define mapName.s   = "SharedMapName"
Define mutexNumber.i
Define readEventNumber.i
Define sendEventNumber.i
Define sendStr.s   = ""
Define mapSize.i   = 16384
Define stringLen.i

readEventNumber = CreateEvent_(0, 0, 0, "read_" + mapName + "_event")
sendEventNumber = CreateEvent_(0, 0, 0, "send_" + mapName + "_event")
mutexNumber     = CreateMutex_(#Null, #True, mapName + "_mutex")

; procedure to write data to the shared memory map

Procedure.i WriteMapString(*mapName, *sendMemory, stringLen)

    Protected hFileMap.i
    Protected *mapView
    Protected retValue.i = #False

    hFileMap = OpenFileMapping_(#FILE_MAP_WRITE, 0, *mapName)
    If hFileMap
        *mapView = MapViewOfFile_(hFileMap, #FILE_MAP_WRITE, 0, 0, 0)

        If *mapView
            CopyMemory(*sendMemory, *mapView, stringLen)
            PokeA(*mapView + stringLen, 0)
            retValue = #True

            UnmapViewOfFile_(*mapView)
        Else
            retValue = -2
        EndIf
        CloseHandle_(hFileMap)
    Else
        retValue = -1
    EndIf

    ProcedureReturn retValue

EndProcedure

; use the #INVALID_HANDLE_VALUE to tell the system to use memory, not a physical file!

hMap = CreateFileMapping_(#INVALID_HANDLE_VALUE, #Null, #PAGE_READWRITE, 0, mapSize + 1, @mapName)

OpenConsole("Shared Map Producer, version: " + version)

sendStr   = "Started Shared Map Producer"
stringLen = Len(sendStr)
result    = WriteMapString(@mapName, @sendStr, stringLen)
ReleaseMutex_(mutexNumber)
If result > 0
    PrintN("Send to shared Map, initial value sent is: " + sendStr + ", packet sent ok")
Else
    PrintN("Error writing data to shared map, process will terminate, result = " + Str(result))
    Input()
    End
EndIf

Repeat
    Print("Enter text To send, or press <enter> to quit: ")
    sendStr   = Input()
    stringLen = Len(sendStr)

    If stringLen > 0
        If  stringLen > mapSize
            stringLen = mapSize
            sendStr   = LSet(sendStr, stringLen)
        EndIf

; wait until it is able to aquire a mutual exclusive lock, before writing to the shared memory, then release the exclusive lock

        WaitForSingleObject_(mutexNumber, #INFINITE)
        result = WriteMapString(@mapName, @sendStr, stringLen)
        ReleaseMutex_(mutexNumber)

; now release the mutex, set an event telling the consumber process it should do a read, then wait again until read is done

        SetEvent_(readEventNumber)
        WaitForSingleObject_(sendEventNumber, #INFINITE)
    Else

; set an event here so that the consumer process can terminate gracefully

        SetEvent_(readEventNumber)
        Break
    EndIf
ForEver

CloseHandle_(hMap)

CloseConsole()

End
; IDE Options = PureBasic 5.42 LTS (Windows - x64)
; CursorPosition = 4
; Executable = shm_producer.exe
; DisableDebugger
; HideErrorLog
; CompileSourceDirectory
; EnableCompileCount = 12
; EnableBuildCount = 12
; EnableExeConstant

Code: Select all

; shm_consumer.pb

EnableExplicit

Define version.s = "1.0.02b"
Define lenSent.i
Define sentText.s
Define mapName.s   = "SharedMapName"
Define mutexNumber.i
Define readEventNumber.i
Define sendEventNumber.i
Define eventNumber.i
Define firstTime.i = #True
Define *recvString = AllocateMemory(16384)

readEventNumber = CreateEvent_(0, 0, 0, "read_" + mapName + "_event")
sendEventNumber = CreateEvent_(0, 0, 0, "send_" + mapName + "_event")
mutexNumber     = CreateMutex_(#Null, #False, mapName + "_mutex")

; procedure to open he shared memory map, and read the data

Procedure.i GetMapPointer(*mapName, *recvMemory)

    Protected lenSent.i = 0
    Protected *mapView
    Protected hFileMap.i
    Protected sentText.s

    hFileMap = OpenFileMapping_(#FILE_MAP_READ, 0, *mapName)

    If hFileMap
        *mapView = MapViewOfFile_(hFileMap, #FILE_MAP_READ, 0, 0, 0)
        If *mapView
            sentText = PeekS(*mapView)
            lenSent  = Len(sentText)
            UnmapViewOfFile_(*mapView)
        EndIf

        CloseHandle_(hFileMap)

        If  lenSent
            lenSent = PokeS(*recvMemory, sentText, lenSent)
        EndIf
    EndIf

    ProcedureReturn lenSent

EndProcedure

OpenConsole("Shared Map Consumer, version: " + version)

Repeat

; wait for an exlusive lock here, to make sure reads and writes complete, then read in the data, and release the lock

    WaitForSingleObject_(mutexNumber, #INFINITE)

    lenSent = GetMapPointer(@mapName, *recvString)
    If lenSent > 0
        sentText = PeekS(*recvString, lenSent)
        ReleaseMutex_(mutexNumber)

        PrintN("Read Map = " + Str(lenSent) + " bytes received: " + sentText)

        If  firstTime
            firstTime = #False
        EndIf

; tell the consumer process it is ok to write more data now if needed, then wait for the next time to read in data

        SetEvent_(sendEventNumber)
        WaitForSingleObject_(readEventNumber, #INFINITE)
    Else
        ReleaseMutex_(mutexNumber)
        Break
    EndIf
ForEver

If firstTime
    PrintN("The shared memory producer must be started first!")
    Input()
EndIf

CloseConsole()

End
; IDE Options = PureBasic 5.42 LTS (Windows - x64)
; CursorPosition = 74
; FirstLine = 18
; Executable = shm_consumer.exe
; DisableDebugger
; HideErrorLog
; CompileSourceDirectory
; EnableCompileCount = 14
; EnableBuildCount = 14
; EnableExeConstant
Post Reply