Re: SNAPI Floundering App – Joe Please Help

From: RNEELY (RNEELY_at_discussions.microsoft.com)
Date: 09/01/04


Date: Wed, 1 Sep 2004 15:15:05 -0700

Dr. Newcomer,
Thank you sooo much for your detailed response. Things are starting to
appear much clearer now. I didn't realize the "SNAPI" program, i.e.:
A();
B();
C();
is still an AFSM that implicitly stores its state as a combination of its
instruction pointer and stack pointer. I can now see that making the AFSM
explicit could lead to much clearer and more robust code. Alan Turing would
probably agree.

I wonder if it is worth it to spawn new a ASFM on UI "worker" thread for
each new long sequence of sequential work, i.e. A();B();C();. The primary
ASFM would know how to enable/disable menu items based on notifications it
would receive from the worker ASFMs.

After reading your article on I/O Completion Ports
http://www.flounder.com/iocompletion.htm it seems the main reason for using
them is to guarantee responsiveness of the GUI under heavy I/O load. I don't
think the app in question necessarily has extreme or bursty network traffic.
My hopefully not too naive hope is that after I take the PeekMessage() loops
out the app will perform good enough so that your clean and simple example
will work:

CString * s = new CString;
s->Format(_T("The answer is %d"), answer);
wnd->PostMessage(UWM_MSG, (WPARAM)s);

// In the receiving window:
ON_REGISTERED_MESSAGE(UWM_MSG, OnMyMessage)
LRESULT CMyView::OnMyMessage(WPARAM wParam, LPARAM)
   {
    CString * s = (CString *)wParam;
    c_Log.AddString(*s);
    delete s;
    return 0;
   }

Thanks again.
Regards,
-Ron

"Joseph M. Newcomer" wrote:

> See below...
> On Tue, 31 Aug 2004 07:39:09 -0700, RNEELY <RNEELY@discussions.microsoft.com> wrote:
>
> >Joe,
> >I really appreciate the articles on your website: http://www.flounder.com -
> >especially the ones about multithreading in MFC. In many of those articles
> >you discuss how you have never seen a proper implementation of a
> >PeekMessage() loop. Would you consider answering some more questions?
> >1) Why is PeekMessage() so bad? I appreciate the advice in your article but
> >it is not enough to convince my boss a rewrite may be needed.
> ****
> Several reasons. First, PeekMessage takes time, making it inappropriate in a
> high-performance loop. Second, and more important to me, if you fail to do PeekMessage
> often enough, the GUI appears to be locked anyway, because it is so sluggish.. Finally,
> PeekMessage always represents a "whoops", in that the design was created based on the
> assumption that all operations took zero time, and when it is discovered the GUI must
> remain alive, this kludge is shoved into the program in an attempt to work around what is
> a fundamental design flaw. The consequence of this afterthought is that now the GUI is
> fully live, meaning you can now allow the user to do things that should not have been done
> because there were no explicit prohibitions, and finding and fixing all of those problems
> retroactively usually leads to convoluted code that often isn't fully debugged because
> nobody sits down and tries to activate every menu item while the loop is running.
>
> Back in the days of Win16, we HAD to do PeekMessage or the whole SYSTEM was hung during
> long comuptations, and these problems were with us all the time.
> ****
> >2) How can one implement a custom “Synchronous Network API” (SNAPI) that is
> >not a candidate for your “n Habits of Highly Defective Windows Apps” article?
> ****
> One word: Asynchronous Finite State Machine (OK, I can't count). The issue you raise is
> baded on the sequential computation model: I do A, and when A completes I do B, and when B
> completes I do C. This is expressed in the program
> A();
> B();
> C();
> Now that's fine if A, B and C have no other interaction with the world. But if one of them
> takes a long time, the effect on the outside world is that the GUI is dead for the
> duration of that operation. Note that if they are doing synchronous I/O, the presence of
> PeekMessage is irrelevant..
>
> The trick is to either use a separate thread or use asynchronous I/O. The way this is done
> is to have the computation in a particular state, say state A. In state A, an action to do
> A() is initiated. Whether this is done by activating a thread in some way, or via
> asynchronous I/O, doesn't matter much at this point. When the notification is received
> that A has finished, which is now independent of the flow of control of the execution of
> the program, you initiate action B. When you receive a notification that has completed,
> initiate C. Now, from the viewpoint of the outside world, the perceived effect is that A
> has sent out a message (such as SNMP), at some point in the possibly distant future the
> device responds, and upon the receipt of the response the device receives message B. And
> so on. But from the viewpoint of the program, things are progressing asynchronously, and
> the GUI is live. Because this is usually designed in, the decisions as to what can and
> cannot be activated during the transaction become evident at design time, and issues like
> what happens if File>Exit is selected become obvious.
>
> I would probably use I/O Completion Ports with a thread simply sending notifications to
> the main GUI thread to initiate the next phase of the operation. If I used a separate
> thread to initiate the I/O, I would still use asynchronous I/O, but my WaitFor would be
> WaitForMultipleObjects on an array whose 0th element was used to terminate the operation
> prematurely (see my essay on worker threads and clean thread termination).
> *****
> > What I mean by SNAPI is a programming paradigm wherein a client app can make
> >a request to a server and properly block waiting for a response before
> >continuing on with the next request.
> *****
> No, as explained above, you have confused an implementaiton detail (blocking I/O) with a
> logical concept (do not initiate an action until the previous action has completed). When
> we have bounded latency with small time constants (e.g., reading from a disk), we can
> write implementations that simplify the problem by ignoring the time constants of the
> device entirely because they are perhaps in the worst case in the tens of milliseconds),
> but whenever there are external devices with unbounded latency (including infinite
> latency, say, the device stops working and fails to respond) then the notion that coupling
> the waiting for the response with the concept of blocking the active thread fail utterly.
> *****
>
> >3) Is a “SNAPI” generally just a bad idea? If so, why?
> *****
> SNAPI is an idea. Bad or good is not something that can apply here. There are many
> excellent reasons to use SNAPI (it is a well-understood protocol) and some excellent
> reasons to avoid it (it sends passwords unencrypted on the network, or did the last time I
> looked, making the devices it controls vulnerable to anyone with a packet sniffer and too
> much free time). It is used in lots of contexts because it is easy to implement on an
> embedded device, serves a need well, and/or is a standard. And if you need to control a
> SNAPI device, you don't have a choice anyway. This is the situation you are in.
>
> OTOH, if you are doing computer-to-computer communication, application-to-application, it
> is just silly. There are LOTS better ways to send structured messages across a link
> reliably. Using XML across TCP/IP is a nice start. In such cases the marginal value of
> SNAPI in terms of standards is questionable, unless your app is emulating an embedded
> device.
> *****
> >
> >I have been fortunate enough to inherit support for a very large MFC client
> >application that communicates with an embedded server via a custom
> >proprietary binary protocol. Most of the client application counts on a
> >proprietary implementation of a “SNAPI”, which works as follows:
> >a) the client app makes a function call on its main GUI thread to issue a
> >request to the server
> >b) the client function sends a message to the embedded server and
> >enters a PeekMessage() loop while waiting for response packet(s) from the
> >server
> >c) the client function returns after either:
> > - it receives all of the response packet(s) from the server
> > - it times out before receiving all of the response packet(s) from the
> >server.
> ****
> I'd get rid of the PeekMessage loop, use an I/O Completion Port (won't work on MS-DOS, by
> the way), and I would feel a lot happier. The last time I got a program like this from a
> client, one of the complaints was that if the embedded device hung, there was no way to
> stop the program. It took me about three days to recode it to use the I/O Completion Port
> approach (it only was intended to run on Win2K and above), and more than halt that time
> was spent checking out every menu item to see what would happen if a menu item was
> activated when the GUI was active
> ****
> >
> >The app and the binary protocol require specific long sequences of binary
> >requests and responses to perform meaningful work. Each request /
> >response(s) pairing counts on all previous request / response(s) pairings to
> >have succeeded.
> *****
> Not a problem using an AFSM (OK, that's one word)
> *****
> >
> >Most of the code in this MFC app counts on a programming model where it can
> >issue a request, block waiting for a response while still pumping GUI
> >messages, and then continue on with the next request.
> *****
> That was just the wrong approach. What you have is an AFSM without the clean
> implementation model of an AFSM.
> *****
> >
> >4) Perhaps these long sequences of work should be moved from the main GUI
> >thread and put on separate UI worker threads. Still then, how would one
> >implement a “SNAPI” for each of these UI worker threads?
> *****
> You only need one thread, and that's a single thread connected to the I/O Completion Port.
> Even if you did it in other ways, you should only require one thread. Or, if you have
> multiple devices, you might want to have one thread per device. Note my trick of
> subclassing OVERLAPPED to add additional information, such as the device and its state. If
> what you do is PostMessage the pointer to the subclassed OVERLAPPED structure, then the
> AFSM becomes very straightforward to implement. It also means that if you choose to use
> the I/O Completion Port, you really only need one thread, and each operation carries its
> own state with it. The packet circulates around between your GUI thread and the I/O
> Completion Port thread until the operation completes. Note that the major change is that
> flow control of your transmission is now embedded in the data structure and its
> interpretation, rather than in the code itself.
> *****
> >
> >Regards,
> >-Ron
> >Ron Neely, Senior Software Engineer, Orbacom Systems, Inc.
>
> Joseph M. Newcomer [MVP]
> email: newcomer@flounder.com
> Web: http://www.flounder.com
> MVP Tips: http://www.flounder.com/mvp_tips.htm
>



Relevant Pages

  • Re: SNAPI Floundering App Joe Please Help
    ... >1) Why is PeekMessage() so bad? ... often enough, the GUI appears to be locked anyway, because it is so sluggish.. ... >continuing on with the next request. ... It took me about three days to recode it to use the I/O Completion Port ...
    (microsoft.public.vc.mfc)
  • Re: The crux of peoples issues with PLT Scheme?
    ... Nobody cares whether a GUI (that is typicially written in C or C++ and ... happening in response to interactive input, ...
    (comp.lang.scheme)
  • Re: Cryptogram Comment
    ... GUIs, code the callbacks and have nice flashy pretty gui applications. ... Response as in "look we can do it too!" ... > it and to provide the fix FOC for paid-up customers. ... > to all users, including pirates. ...
    (sci.crypt)
  • Re: Handling outputs (yes, an *on* topic thread!)
    ... Perhaps, in the future, ordering a part would create as response for ... >> column that gives the sequence of messages, and rows within a message, by ... Anything else will be deleted when the user logs out ... Like I said, for the GUI, we're already using a table. ...
    (comp.lang.cobol)
  • Re: Error 7 - Out of Memory Error
    ... Thanks for the response. ... The error is actually occuring when the GUI tries to instantiate one of ...
    (microsoft.public.vb.general.discussion)

Loading