Re: Daemonizing CRM114 using FIFO

James Lee <csejl-/[email protected]>
Newsgroups gmane.mail.spam.crm114
Message-ID <[email protected]>
Thanks for your detailed response.  However, my question wasn't so much about how to solve this sort of problem in general but about how to do it specifically in CRM114.

First, let me tell you that this will be an asynchronous solution.  When it's all said and done, the client will send a unique ID along with every message and the :do_it: CRM function will parse the classify result and write the unique ID and its category number to result.txt.  There will be another process (a thread actually) that reads the result file at a later time.

I thought about using WINDOW statement to parse the content into messages but it can only read from stdin, correct?  In other words, can I use WINDOW statement to read the input from a FIFO?  Reading from a file is not an option for me...

Let's say I go with the lengh+content solution where 1+ message(s) are delivered at a time via a FIFO.  How would I write my :do_it: function so that when it's called (close() is received) it outputs the correct number of results before exiting?  Would I use MATCH and LIAF?  An example would help a lot since this would be my first attempt at writing a CRM114 script.


Thanks!

-James


----- Original Message ----
From: Ger Hobbelt <[email protected]>
To: James Lee <csejl-/[email protected]>
Cc: [email protected]
Sent: Monday, September 22, 2008 2:22:39 PM
Subject: Re: [Crm114-general] Daemonizing CRM114 using FIFO

I don't know how well versed you are will this sort of stuff
(communication protocol design), so I'll start at the beginning:


FIFO's are streams and suffer from the same issue as, for instance,
TCP streams. Using stream protocols to transfer messages is always a
hassle - as you found out.

Now there are a two levels of trouble, (a) one which is 'easily'
solved, (b) the other is much much harder to do 'properly'.


(a) is 'delineating' a message - the 'end of message' marker. Two
solutions there are used very often in generic communication
protocols:

1)- either define your messages as 'length + content': each message
starts with a 'header' which lists the number of 'content' bytes that
will follow. The easiest way when you go this route is to have the
header have a FIXED size. Since CRM114 is quite good at chopping text,
you might think up a header like, for instance, one line of text,
containing the length (in characters = bytes) of the content
following. To make 'looking at' the stuff while debugging a bit easier
you may consider adding a 'terminating' LF (end of line) character
after the content - it's not necessary this way, but it nicely
outlines the 'length' specifying header line when you look at the raw
traffic when debugging.

You can use crm114 script commands to 'cut out' the content again once
you've let CRM114 decode that length. It doesn't even HAVE to be fixed
as, using the example above, you can use the extra LF characters in a
regex to grab&decode the header for each message.

2)- another way communication protocols handle the 'delineation' issue
is through 'escaping' special codes - like you asked for. This method
does not require a 'header', nor the specification of a 'message
length' anywhere. Say we declare byte value 0 (ASCII NUL) to be our
EOM (End Of Message) marker. That means we can't have a NUL in our
content... unless we escape any NULs in there. For that, we need
/another/ byte value to signal the receiver an 'escape' is happening.
Assume we use ESC (byte value 27(decimal)) for that PLUS (as now ESC
can't show up in the transmitted content either) the agreement that
byte value 0 in content --> ESC '0' (ASCII ZERO = 48(decimal))
byte value ESC(27) in content --> ESC 'E'
(This is an example.)

Given those rules (very many protocols from the time when terminals
and RS232 lines were hot stuff work like this) we can transmit ANY
byte value in a message content, while a lone NUL(0) unambiguously
signals End Of Message.

Sample 7 byte message {ESC 'P' NUL "Hello"} would thus read as
(INCLUDING the End Of Message Marker!) when transmitted:

ESC 'E' 'P' ESC '0' 'H' 'e' 'l' 'l' 'o'
NUL

Again, CRM114 can handle this kind of thing using regexes to alter
those escapes back into the original byte values as soon as you've
isolated the individual messages.

----

These are the two basic solutions for 'delineating' your message -
they are not specific to CRM114, as both are generic concepts used in
a lot of communication protocols where messages have to be extracted
from data streams.

Of course, you can take the 'escape' idea and take it further like
Internet email once did: encode all content as base64 (the
historically sensitive would suggest UUencoding the data here ;-) ),
which leaves you a whole slew of byte values to use for End Of Message
markers and such (in fact: all byte values < 32, excluding CR and LF),
plus all the 'high ascii' values ie byte values >= 128(decimal).



(b) Which leaves the second issue, which you already ran into and
resolved by open+close on the one side plus wait on the other: this is
the hard nut to crack: asynchronous versus synchronous communications.

I can't provide you with any truely portable solution for it when
excahnging messages over stream protocols while 'keeping the
connection open', because there isn't. Several systems out there offer
'flushing' API calls which work on FIFO's; some systems can be
'tormented' into flushing outgoing data by cutting down the transmit
buffers to the very bare minimum (check your ioctl() and related
APIs); some systems cannot be tricked in any useful way but by
'padding' messages with extra data, that is: enough extra data to
ensure that particular system's buffers WILL be flushed by 'pushing
out' the message content through /overflowing/ the system buffers with
'padding bytes': the worst I've seen was 8Kbyte padding per message
and I am still not sure if that was really necessary given a bit of
proper research but the guy who wrote it was short on time and low on
OS knowledge. It worked for the sub-1K messages the system was
transmitting.

If you do 'padding', you can use anything, but it is useful to use a
'special byte code' for that as well, say FF (hex), so that the
receiver, who WILL have to handle all that extra data, at least can
quickly recognize the padding for what it is: fluff.


Why do I address this issue (b) separately and at all?

Because you should ask yourself if your system needs 'synchronous' or
'asynchronous' processing by CRM114: in other words: do you need to
receive the answer for each message before you can submit the next
(which means you're stuck with 'synchronous' communication)? Or can
you submit several messages without the need to receive the first
answer yet; just as long as the /order/ of transmitted messages is the
/same/ /order/ in which the answers return (==> asynch communication
is allowed)?

In the latter case, you can do without the waiting for each message;
you can transmit all messages, each properly delineated in one of the
two fashions listed above, and have CRM114 process them batch-wise,
returning the answers in order, though delayed -- as CRM114 will wait
for the 'input' to complete (= FIFO closed) before processing the
input. In the latter case, you basically construct batches for CRM114:
it'll be fast, but you have to ensure the total batch stays within the
buffer limits for CRM114 data processing -- see the CRM114 command
line options (I think it's -s, but not sure and can't check right now)
for when you wish to see or change that setting.


Does this help you along?







On Mon, Sep 22, 2008 at 7:33 PM, James Lee <csejl-/[email protected]> wrote:
> Hi,
>
> I'm in the process of daemonizing CRM114 and had a few questions.  First, here's what I have so far:
>
> - My C program (server) creates a FIFO (/tmp/dynamic_fifo) and runs this command: /usr/bin/crm /home/me/dynamic.crm < /home/me/start.txt
>
> - /home/me/dynamic.crm looks like this:
>
> {
>    isolate (:content:)
>
>  # classify dummy content to load the css files so that we don't have
> to reinitialize repeatedly in the :do_it: function below
>
> classify <osb unique microgroom> (\/home\/me\/css\/A.css
> \/home\/me\/css\/B.css \/home\/me\/css\/C.css \/home\/me\/css\/D.css)
> (:content:)
>    :loop:
>    syscall /:do_it: <\/tmp\/dynamic_fifo >>\/home\/me\/result\/result.txt /
>    goto /:loop:/
>    exit
> }
> {
>    :do_it:
>    input
>
>  classify <osb unique microgroom> (\/home\/me\/css\/A.css
> \/home\/me\/css\/B.css \/home\/me\/css\/C.css \/home\/me\/css\/D.css)
> (:_dw:)
>    output /lala :*:_dw:\n/
>    exit
> }
>
> - Another C program (client) opens the FIFO in write-only mode and writes some content (a "message" to be classified) in a loop.
>
> The
> first issue I'm having is that I have to open() and close() the FIFO
> every time I write inside the loop.  My finding is that the close()
> call simulates typing ctrl-D on the console.  The second issue is that
> I have to usleep() or run a dummy for-loop after the close() to make
> sure CRM114 receives and acts upon one chunk of data at a time.
> Without the wait, my :do_it: function only gets called once.  I think
> this has to do with how the kernel buffers and writes to FIFO.  It
> appears that it ignores the close() if the same FIFO is opened and
> written right away.
>
> My goal is to have the server program run
> forever and have the client send as many messages as possible as fast
> as possible.  Having to open() and close() every time I send a message
> slows down the process but it's not a biggie.  The wait part after each
> close() seems to be the major performance bottleneck since I can only
> sleep as little as 15-20 ms on the 2.4 kernel and 4-6 ms on the 2.6
> kernel.  The dummy for-loop takes too many CPU cycles and is just as
> slow.  And neither of them is a guaranteed solution.
>
> In this
> model, is there a way for me to use a special word or character to mark
> the end of a message so the CRM114 part doesn't depend on whether
> close() is received or not?  If not, is there a better way to
> accomplish this?
>
> Thanks in advance,
>
> -James
>
> -------------------------------------------------------------------------
> This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
> Build the coolest Linux based applications with Moblin SDK & win great prizes
> Grand prize is a trip for two to an Open Source event anywhere in the world
> http://moblin-contest.org/redirect.php?banner_id=100&url=/
> _______________________________________________
> Crm114-general mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/crm114-general
>
>



-- 
Met vriendelijke groeten / Best regards,

Ger Hobbelt

--------------------------------------------------
web: http://www.hobbelt.com/
http://www.hebbut.net/
mail: [email protected]
mobile: +31-6-11 120 978
--------------------------------------------------


-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.