Subject: On multithreaded cclient

From: "Alex Shvedov" <[email-masked]>
Date: Thu, 10 Dec 1998 09:52:29 -0500

Some time ago I posted a question whether anyone has a multithreaded version
of cclient; nobody responded.

Now I know what changes are necessary to prepare such version, though I
don't have it ready yet (to be exact, I have it, but a crude one, with
locks/unlocks, which is bad).

Is anyone interested? Or I'm the only one?

I work on AIX 4.2.1 (IBM's flavor of UNIX).

Alex Shvedov.

------------------------------------------------------------------
 For information about this mailing list, and its archives, see: 
 http://www.washington.edu/imap/c-client-list.html
------------------------------------------------------------------

---

From: "Alex Shvedov" <[email-masked]>
Date: Mon, 14 Dec 1998 11:15:17 -0500

gethostbyname_r was not referenced in AIX docs, that's the reason for a dumb
workaround.
Once again: my purpose was to find out what is NOT safe. Diagnosis first,
treatment second.
And this was not a problem. Reads/writes were, and call-backs costed much
more.

---

From: "Alex Shvedov" <[email-masked]>
Date: Mon, 14 Dec 1998 12:23:40 -0500

>I don't see how callbacks could be a problem?

As I said before, it can't be explained in two words (same goes for
read/write nonsafety). I'll talk on that later. Give me some time to
recreate the problems, then with God's help I'll remember the solutions...
They were a little bit more than_r adiing_r something_r to_r everything_r...

Alex

---

From: "Alex Shvedov" <[email-masked]>
Date: Wed, 13 Jan 1999 15:12:29 -0500

>Alex,
>
>I also want to use c-client to open connections to multiple email servers,
at the same time. I spawn a >seperate thread for each such connection. Of
course
>the first connection goes through, but the second connection fails.
>
>On a little further examination of the code I found that c-client has some
>static
>variables, and therefore may not be thread safe. It seems that you have
>already
>identified these varaibles and have put a mutex arround them.
>
>Will it be possible for you to give me your fixes, and let me know what I
>should
>know, before I use them?
>
>I have not looked at the c-client code at all, I just use their library.
>
>Narendra K. Gupta
>
>AT&T Consumer Labs            Ph: 973-236-6472
>Room 1K28, Bldg 104           email:[email-masked]
>180 Park Avenue
>Florham Park NJ 07932

Actually, I explained the ideas in my previous letters.
Several comments to what I copy from actual code below.

A. I'm not 100% sure that I pulled out all non-thread safe things; what I
did was appr. what you tried to do: "if it's not working, go change until it
works" kind of job. Still, 3 threads worked together with our 2 different
IMAP servers simultaneously for a couple of hundreds cycles, so the delta
between real persentage and 100 must be pretty small.

B. I used AIX and never even thought about porting the code.

C. Earlier I mentioned 3 items that prevent cclient from being thread-safe.
Of them:

Item 1 (gethostbyname) is absolutely clear.

Item 2 (tcp_unix module) would have been clear also, if I only remembered
what global was so nasty. The only thing I advise you to do is to start
(normal) cclient with some debugger and see who is the culprit.
Vaguely remember that it was the socket FD, but I'm not sure at all.

Item 3 (call-backs) is most difficult to explain.
I work in computer telephony, where the user interface capabilities are much
more limited than usual. Therefore some things that would have been bearable
or even useful in a GUI-based stuff, are an absolute NO for a TUI.
I noticed that sometimes call-back returns reasonable value (say, mail_open
is non-NULL) while there's a warning in the log. This situation might be
good for someone but not for me.
That's why I had to add TWO globals and actually wrap all call-backed calls
into something idiotic like:

// globals:
short GlobRC1, GlobRC2;    // GlobRC1 - actual return code; GlobRC2 is to be
set before each call

MAILSTREAM Strim;

short MailOpen(char *Mbx, long Debug) {
    GlobRC1=IMAP_SUCCESS;                               // for mm_log
call-back
    GlobRC2=IMAP_MBX_OPEN_FAILURE;            // loading the gun
    Strim=mail_open(Strim, Mbx, Debug ? OP_DEBUG : NIL);
    if (!Strim ||  GlobRC1 != IMAP_SUCCESS)
        return (GlobRC1 == IMAP_SUCCESS)?IMAP_MBX_OPEN_FAILURE:GlobRC1;
    else
        return IMAP_SUCCESS;
}

while in mm_log call-back - the only one that feels warnings! - I'm setting
"reserved", prepared beforehand GlobRC2 to something I need IN CASE OF
WARNING (not an error):

void mm_log (char *string, long errflg) {
    char BB[LONG_ENOUGH+1];
    if(strlen(string) > LONG_ENOUGH-20)
        string[LONG_ENOUGH-20]='\0';
    switch ((short) errflg) {
        case NIL:
...................................
        case WARN:
            GlobRC1=GlobRC2;        // report a warning
            break;
...................................
    }
}

I don't know whether it's really necessary in ALL calls (Marc sholud know
this better), but I state that SOME calls do need such wrapping.

Of course, this variant is not thread-safe, but I didn't implement even the
dumbest solution for the return codes' criss-crossing in a call-back; if you
are in the same kind of environment as me, you'll have to do it yourself.

Hint: actually, it's simple (as it is in all three items): I would use
thread-specific structures (in POSIX it's pthread_getspecific,
pthread_getspecific and the like). I call them pseudoglobals. I did so in a
multithreaded server I created and it's not only simple - you manually edit
the code mostly in a mechanical manner - but also fast (!) - OS has to
switch just ONE pointer for you! No downpayment, low fat, low chol, all
natural!

I stopped after a multiprocess (not multithreaded) IMAP client was completed
and left call-backs for good.

I tar/gzip all files that I changed.

Absolutely ALL changes are marked by my initials (AMS) in comments, so you
can just do a text search for AMS.

Not ALL of the changes are thread-related; I didn't want to start rsh (and
made that static), there are some other changes (boring syntax error in
siglocal is corrected, for example).

When I was pretending to be a developer, I put #undef THREAD - #define
THREAD; sometimes, when I did not, I just commented the locks out - sorry.

Enjoy!

Alex Shvedov