News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Buffers/concurrent processing

Started by Astro, March 29, 2010, 11:55:21 AM

Previous topic - Next topic

Astro

Hi,

I suffer from the inability to conceptualize buffers. I know what they are, how they work etc.. but I'll be damned if I can get my head around them long enough to write some code!!!

I need to read a device (now working) a byte at a time, but the data is encapsulated in frames.

The frames are variable in length, and need to be processed to pass the data on to an application.

The problem I'm having is how to start reading the data, store it in a buffer I create to then start processing it.

I'd considered creating a buffer of about 4 kB to throw the data into as it is received, and manage the buffer using pointers.

e.g.

INBUF - buffer
CurrPos - where I got to in processing
EndPos - where I got to in writing
PosWrap - a flag (0/1) as to whether the EndPos wrapped the buffer so I know if I should stop processing or not, reset when CurrPos wraps

To check if the buffer is full for writing I check the wrap flag, then compute the difference in pointer positions, and check against the buffer length. I would do the same for reading.

I need to process the buffer and read/write the device asynchronously, and wondered if multiple threads is the best way to accomplish this?

I know the optimum thread count equals processor count, but will I suffer much of a hit with a thread count that exceeds the processor count?

Is there a race condition I'm not seeing here? Only AFTER some data was processed from the buffer would the write to the application be called from the buffer processing thread, then return and process the next block of data.

Likewise, when taking data from the app and sending it to the device, the write to the device be called from the processing thread, so as far as I can tell, it should never write until it is done processing.

I'll draw a flow diagram so you can see what I'm thinking.

Best regards,
Robin.

Slugsnack

#1
Personally I would limit threads to how many cores are available but I couldn't give you a definite answer for that question. However one thing did strike me immediately when i saw this thread. Because you may be concurrently accessing the same buffer it is very important that you have it so a thread can only execute your function after gaining permission with say via a critical section, semaphore or if this is a kernel device driver then a spinlock would be perfect. Either way if you were to have this mutual exclusion, then the number of threads is not actually so important.. because only one thread would be able to access at once. That should get rid of the problem of causing a race condition during execution. Hence there should theoretically be no advantage of more than 2 threads over 2 threads.

Note, multiple reading of the buffer can be done simultaneously as writes whereas writes must only be performed one at a time. Looking at this briefly, it would seem it is a problem suited to a linked list though. You could have an almost infinite buffer size and removing data would not give you problems of a non-contiguous buffer.

hutch--

Robin,

It sounds like a gadget where you need to do asynchronous IO so in the first instance I would write it as a single thread just to get it going. There are many traps with multithreading here so I would be very careful in the first instance to avoid these and properly seperate reads and writes so you are not trying to do both at once. Further down the track if you can get the logic right you can start doing multiple threads where you synch between the threads to control any overlaps of reads and writes. There are a range of WaitFor.... functions so that you can hold one thread until another finishes that would be worth looking at but also note that thread synchronisation still tends to be slow on x86 hardware and the general drift where possible is to start threads and let them run until they are finished on a given task.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

redskull

Can you be more specific about the device?  What it does, how its connected, which driver it uses, how an application reads/writes to it, how you know when a variable-length frame is complete, etc.

-r
Strange women, lying in ponds, distributing swords, is no basis for a system of government

Astro

Hi,

It is serial, using a serial multiplexing protocol. Start/end frame flags are single byte (0F9h or 11111001b). Frame length max 128 bytes.

Frame structure: FLAG - ADDRESS - CONTROL - LENGTH - DATA - FCS - FLAG

All except DATA are 1 byte each.

Access is via Create/Read/WriteFile.

Quotehow you know when a variable-length frame is complete
As part of the reception of the data I would have have to count the bytes after receiving LENGTH, then I know the next two bytes are the FCS and the end flag of this frame.

The only possible issue I see is detecting the start of a valid frame when I first start receiving data, but as far as I can tell I shouldn't be losing data...

The device has one serial port, and the protocol is written over it and interpreted by it at the other end in hardware. At the computer end I can have up to 4 virtual serial ports for use by various applications simultaneously. Data to/from these ports is numbered internally by my software, and corresponds to a particular DLCI of the frames being sent to/from the device (e.g. VCP #1 would use DLCI 1).

Quotein the first instance I would write it as a single thread just to get it going.
I was going to do this anyway to test that I can manipulate the protocol correctly. At device start-up it is in single com port mode. I have to send a command to start the protocol on it, then I need to switch my app to start using the protocol in order to continue communication with it (the original channel becomes invalid immediately after the command is sent, and appears on DLCI 1).

This is what I was thinking:



Best regards,
Robin.

hutch--

Robin,

Just a quick look but if this data is coming in through a COM port it is so slow that you would set up a monitor thread to check if and when some data is incoming and let it sleep for the rest of the time. This in conjunction with a suspend3ed work thread may be a viable architecture to layout for a task of this type. I confess to not having worked with COM ports for a very long time but I doubt they would pass much over 100k/sec even now. USB would be another matter.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

redskull

I can't recall seeing several virtual COM ports all accessing a singular physical COM port; usually they are for virtualizing to bus topologies (ethernet, USB, etc).  Assuming that the vcps somehow take care of ensuring the correct data stream goes to the right application, then most of what you need is already built into the com port functions; you can set up the flag field as the "event character", and then catch every (or in your case, every other) event using WaitCommEvent (probably in asymchronous mode).  When that happens, just copy out the first few bytes, check the length, copy the data, and keep on going.  For all intensive purposes, the buffers "built into" the COM port are adaquete.

Unless I completly misunderstood the point, and you are actually trying to build the driver portion of code to virtualize the singular COM port on behalf of the applications, in which case it's a horse of a different color.

-r
Strange women, lying in ponds, distributing swords, is no basis for a system of government

Astro

Hi,

@redskull: you're pretty close on both counts actually. I'm writing the code that handles the data between the virtual ports (and thus the applications) and the single physical port.

Obviously I can see from the DLCI when it comes from the hardware which VCP I need to send it to, conversely I know from which VCP I read from which DLCI to assign to write it to the physical port. This is the easy part!  :bg

My problem is knowing when data arrives and to deal with it. I'll have another look at WaitCommEvent and see what it can do.

@hutch: I'd need to monitor 5 ports in total (4 virtual, 1 real).

Best regards,
Robin.

redskull

So, just to be clear, your "virtual COM ports" are pairs which connect the applications to the control program, which controls the device?  Consequently, your control program would have 4 open VCPs to talk to the applications, and 1 'real' COM port to talk to the hardware?  In that case, I would go the overlapped-route: open each COM port in overlapped mode, with a cooresponding 'event'.  Start a read on all five ports, and then WaitForMultipleObjects on the events.  Whenever a read completes, your thread will start up, do some testing on the bytes that were read, and either send incoming hardware data to the right VCP, or package up incoming software data out to the device.  Then it just starts another read and goes back to waiting for the next result.  There are lots of other ways to do it, and that's just my first thought.

-r
Strange women, lying in ponds, distributing swords, is no basis for a system of government

Astro

Hi,

That is ultimately what I'd like.

Currently got the problem that the second write is starting +1 byte into the buffer to send.  :dazzled:

Best regards,
Robin.

redskull

Perhaps flow control characters are being placed into the buffer?  Several COM port settings can control this.  It might be best to replace the output COM port with a normal file (and mirror the input to files as well) for testing purposes, so that you can inspect exactly which bytes it's trying to send.  Also, a misconfigured COM port can wreck havoc with reads and writes.  Hyperterminal can provide an excellent way to send exactly what you want, a byte at a time, to aid in debugging.

-r
Strange women, lying in ponds, distributing swords, is no basis for a system of government

clive

HyperTerminal!?!? That is a truly awful application, something like RealTerm would be much better.
http://realterm.sourceforge.net/

I would also recommend these serial port monitoring tools. With the free version you can see both sides of the communications.
http://www.serial-port-monitor.com/

Astro, are you using a modem by chance? I'm currently working on GSM modems and GPS receivers, both using serial ports. When debugging serial stuff on PC's I tend to have the applications generate log files of the serial transactions so I can analyze or troubleshoot later. It's often nice to be able to enable that in the field too when people run into problems.

Hutch, the USB dongles can often support 460 Kbps, it makes upload 512KB of firmware to a GPS receiver a lot less painful. Back in the day you could get 16550 UARTs on the PC upto 1 or 2 Mbps if you changed the oscillators (PC's normally used 1.8 MHz, the chips supported 24 MHz, as I recall). Some of the RS232 drivers had filters at around 1 Mbps.

-Clive
It could be a random act of randomness. Those happen a lot as well.

hutch--

It makes sense to use USB if you can find a way to access it without melodrama but these later OS versions are no joy to do simple stuff like that any longer. I remember long ago folks using the old COM ports, printer ports and more or less anything they could get data in and out of but they were really slow alongside any of the modern stuff. It will be interesting to see if the much faster USB 3 can be interfaced in a clean and tidy manner because the speed increase is high enough to use crappy high level code and still have a higher data transfer rate than most of the old stuff.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Astro

Hi Clive,

Thanks for the hint on the COM port monitor! I'll check it out ASAP - still recovering from a HD failure, and accidental destruction of my backup drive (OOPS). My data is still there though.  :red

@redskull: To the best of my knowledge flow control is OFF. When I get my source code back I'll post up what I've got.

@hutch: I long for the days when I/O was easy (even if it was slower). USB is just a total mess IMHO.

Best regards,
Robin.

Astro

Hi,

_DCB STRUCT
    DCBlength DWORD 0
    BaudRate DWORD 0
    fBinary DWORD 0
    fParity DWORD 0
    fOutxCtsFlow DWORD 0
    fOutxDsrFlow DWORD 0
    fDtrControl DWORD 0
    fDsrSensitivity DWORD 0
    fTXContinueOnXoff DWORD 0
    fOutX DWORD 0
    fInX DWORD 0
    fErrorChar DWORD 0
    fNull DWORD 0
    fRtsControl DWORD 0
    fAbortOnError DWORD 0
    fDummy2 DWORD 0
    wReserved WORD 0
    XonLim WORD 0
    XoffLim WORD 0
    ByteSize BYTE 0
    Parity BYTE 0
    StopBits BYTE 0
    XonChar BYTE 0
    XoffChar BYTE 0
    ErrorChar BYTE 0
    EofChar BYTE 0
    EvtChar BYTE 0
    wReserved1 WORD 0
_DCB ENDS

.data
    Dcb _DCB <0>
    CommState BYTE "baud=115200 parity=N data=8 stop=1 xon=off",0

.code

; ¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤

start:

    ;---------------------------------------------
    ; Open a comm port

    mov eax,[offset CRC8]
    mov eax,[eax]
    print str$(eax),13,10
   
    print "Opening com port..."

    push 0
    call SetLastError

    lea eax,ComPort
    push eax
    call OpenComPort
    mov hCom,eax

    mov RetVal,eax
    call GetLastError
    mov LastErr,eax
    print "Done.",13,10
    print "Ret Val: "
    print str$(RetVal),13,10
    print "OpenAsyncComPort Last Error: "
    push LastErr
    call GetErrorCode

    ;---------------------------------------------
    ; Get COM Port State

    print "Building COM port state..."

    push 0
    call SetLastError

    lea eax,Dcb
    push eax
    push hCom
    call GetCommState

    mov RetVal,eax
    call GetLastError
    mov LastErr,eax
    print "Done.",13,10
    print "Ret Val: "
    print str$(RetVal),13,10
    print "GetCommState Last Error: "
    push LastErr
    call GetErrorCode

    ;---------------------------------------------
    ; Build the DCB structure from a string
   
    print "Populating DCB Structure..."

    push 0
    call SetLastError

    lea eax,Dcb
    push eax
    lea eax,CommState
    push eax
    call BuildCommDCB

    mov RetVal,eax
    call GetLastError
    mov LastErr,eax
    print "Done.",13,10
    print "Ret Val: "
    print str$(RetVal),13,10
    print "BuildCommDCB Last Error: "
    push LastErr
    call GetErrorCode

    ;---------------------------------------------
    ; Set the comm state from the built state

    print "Setting Comm Port State..."

    push 0
    call SetLastError

    lea eax,Dcb
    push eax
    push hCom
    call SetCommState

    mov RetVal,eax
    call GetLastError
    mov LastErr,eax
    print "Done.",13,10
    print "Ret Val: "
    print str$(RetVal),13,10
    print "SetCommState Last Error: "
    push LastErr
    call GetErrorCode


Best regards,
Robin.