How to speed up listener?

Hi folks!

At first, I’m very glad to see this online conference!

And my question is next. Assume I have two programs - Prg1 and Prg2. Prg2 is
running in foreground mode, and “Receive” a messages in circle:

pid_t from = 0;

/* some code /

<1> while(1) {
<2> if((from = Receive(0, msgbuf, sizeof(msgbuf))) {
<3> /
prepare reply buffer /
<4> …
<5> Reply(from, replybuffer, strlen(replybuffer));
<6> /
some code */
<7> …
<8> }

Prg1 issues a Send-calls to Prg2. Assume, I have a very many processes like
Prg1, and only one listener Prg2. I had tryed to insert “fork” call in the
Prg2 between lines <2> and <3>, end “exit”, between lines <7> and <8>. But,
when Prg1 call “Send” after first successfull “Send” call, it return a error
message:
No such process.
Can I use a fork() to speed up listener, and process each Send-call
individualy?

Thank you, and sorry for my English.
Ruslan.

Is it possible that Prg1 is, on the second call, sending to the
deceased task that replied to it?

Richard

Ruslan Satlykov wrote:

Hi folks!

At first, I’m very glad to see this online conference!

And my question is next. Assume I have two programs - Prg1 and Prg2. Prg2 is
running in foreground mode, and “Receive” a messages in circle:

pid_t from = 0;

/* some code /

1> while(1) {
2> if((from = Receive(0, msgbuf, sizeof(msgbuf))) {
3> /
prepare reply buffer /
4> …
5> Reply(from, replybuffer, strlen(replybuffer));
6> /
some code */
7> …
8> }

Prg1 issues a Send-calls to Prg2. Assume, I have a very many processes like
Prg1, and only one listener Prg2. I had tryed to insert “fork” call in the
Prg2 between lines <2> and <3>, end “exit”, between lines <7> and <8>. But,
when Prg1 call “Send” after first successfull “Send” call, it return a error
message:
No such process.
Can I use a fork() to speed up listener, and process each Send-call
individualy?

Thank you, and sorry for my English.
Ruslan.

“Richard R. Kramer” wrote:

Is it possible that Prg1 is, on the second call, sending to the
deceased task that replied to it?

Richard

Yes, that’s right. But why Prg2 exit after replying? The next code looks to be
correct (for me):

while(1) {
/* receive a message /

/
and fork for some work, and reply /
fork();

/
after reply, child process exiting /
exit(0);
/
and infinite loop continues */
}

Child should exit, of course. But, as seems to me, on “exit” call, Prg2
exiting…

Well, I had tryed to use threads. For each successfull Receive I had called some
function for request processing, It working, but after 100 Receives
programm exiting with message:

“Resource temporaly unavalable” (produced by pthread_create).
I will continue looking for possible decisions.

Thank you.

Ruslan Satlykov wrote:

“Richard R. Kramer” wrote:

Is it possible that Prg1 is, on the second call, sending to the
deceased task that replied to it?

Richard


Yes, that’s right. But why Prg2 exit after replying? The next code looks to be
correct (for me):
fork() starts a replica child process with a different pid. Without

some way for the child to know it should behave differently, it will
do what the parent was designed to do.

I have never needed to use threads, so I can’t comment on that.

Richard

while(1) {
/* receive a message /

/
and fork for some work, and reply /
fork();

/
after reply, child process exiting /
exit(0);
/
and infinite loop continues */
}

Child should exit, of course. But, as seems to me, on “exit” call, Prg2
exiting…

Well, I had tryed to use threads. For each successfull Receive I had called some
function for request processing, It working, but after 100 Receives
programm exiting with message:

“Resource temporaly unavalable” (produced by pthread_create).
I will continue looking for possible decisions.

Thank you.

fork() starts a replica child process with a different pid. Without
some way for the child to know it should behave differently, it will
do what the parent was designed to do.
Yes, I think you right.



I have never needed to use threads, so I can’t comment on that.
OK > :slight_smile:

Richard, thank you.
Ruslan.

“Ruslan Satlykov” <ruslics@yahoo.com> wrote in message
news:39D6463C.C300ADF4@yahoo.com

“Richard R. Kramer” wrote:

Is it possible that Prg1 is, on the second call, sending to the
deceased task that replied to it?

Richard


Yes, that’s right. But why Prg2 exit after replying? The next code looks
to be
correct (for me):

while(1) {
/* receive a message /

/
and fork for some work, and reply */
fork();

You can’t to that, the process that does the reply
must be the same as the one that did the receive.
When you exit(), fromt he sender point of view, the
receiver died…


/* after reply, child process exiting /
exit(0);
/
and infinite loop continues */
}

Child should exit, of course. But, as seems to me, on “exit” call, Prg2
exiting…

Well, I had tryed to use threads. For each successfull Receive I had
called some
function for request processing, It working, but after 100 Receives
programm exiting with message:

“Resource temporaly unavalable” (produced by pthread_create).
I will continue looking for possible decisions.

pthread_create? In QNX4? are you using the pthread library?

You have a TCP/IP background don’t you?

What kind of job does each “thread” does. The design you
choose is not really suited for QNX4. Give more details about
the work that needs to be done so we can help you better.

Thank you.

Hello Mario,

pthread_create? In QNX4? are you using the pthread library?
Yes, that’s right. I had get a copy of library from QNX site (free

products).

You have a TCP/IP background don’t you?
Yes. How did you know?



What kind of job does each “thread” does. The design you
choose is not really suited for QNX4. Give more details about
the work that needs to be done so we can help you better.

Well. I have a one listener (called Server), and some piece of processes,
which I use for gathering some kind of data from devices (I/O modules).
Server have two sides:

  • client side (TCP/IP service, for user applications - OPC servers, etc.)
  • data side (Send/Receive/Reply service from data-gather processes).

I can have a very many processes, with very high data update period. So, I
can’t say, how many requests the Server should process. And I want to select
a best way for this :slight_smile:.

Mario, thank you.
Ruslan.

“Ruslan Satlykov” <ruslan@tecon.ru> wrote in message
news:8rc49n$8ri$1@inn.qnx.com

Hello Mario,

pthread_create? In QNX4? are you using the pthread library?

Yes, that’s right. I had get a copy of library from QNX site (free
products).

Free but unsupported, has been a beta tag forever :wink:
Many of the C functions are NOT thread safe…

You have a TCP/IP background don’t you?
Yes. How did you know?

What you are trying to do is typical of TCP/IP and Unix
programming. In the QNX4 world threads aren’t use much,
because they aren’t really supported by the C libraries.

What kind of job does each “thread” does. The design you
choose is not really suited for QNX4. Give more details about
the work that needs to be done so we can help you better.

Well. I have a one listener (called Server), and some piece of processes,
which I use for gathering some kind of data from devices (I/O modules).
Server have two sides:

  • client side (TCP/IP service, for user applications - OPC servers, etc.)

These client do they run on QNX4 or other OS?

  • data side (Send/Receive/Reply service from data-gather processes).

I can have a very many processes, with very high data update period. So, I
can’t say, how many requests the Server should process. And I want to
select
a best way for this > :slight_smile:> .

I’m sorry but I don’t have enough information to help me understand
exactly what you want to acheive.

Mario, thank you.



Ruslan.

Server have two sides:

  • client side (TCP/IP service, for user applications - OPC servers,
    etc.)

These client do they run on QNX4 or other OS?
On QNX4.



I’m sorry but I don’t have enough information to help me understand
exactly what you want to acheive.
OK. I’ll try to describe more exactly (but sorry for my English). Typicaly,

it is a industrial controller, with Advantech’s PCM-4823 CPU board, having a
8Mb of RAM, DiskOnChip 4Mb, and PC/104 connector. The small adapter is
connected to PC/104 bus, and we have a set with 16 different analog and
discrete modules on this bus; each contain up to 48 I/O channels. Each
module needs to be queryed every 10-20 milliseconds.
I want to use a threads, one for each module, but as you tell, it’s not too
safely… I want have a just one server process, because of better
coordination at all… And I need to have a link with user applications
throw TCP/IP - SCADAs, monitoring software etc.

Ruslan.

“Ruslan Satlykov” <ruslics@yahoo.com> wrote in message
news:8rcul4$qt1$1@inn.qnx.com

Server have two sides:

  • client side (TCP/IP service, for user applications - OPC servers,
    etc.)

These client do they run on QNX4 or other OS?
On QNX4.

Ha, so client and server runs on QNX4, thus it’s much simpler to use
QNX4 native IPC (S/R/R) for both!


I’m sorry but I don’t have enough information to help me understand
exactly what you want to acheive.
OK. I’ll try to describe more exactly (but sorry for my English).

Don’t worry about your English, your doing a good job. I’m french,
sometimes I myself can’t read what I wrote in English

Typicaly,
it is a industrial controller, with Advantech’s PCM-4823 CPU board, having
a
8Mb of RAM, DiskOnChip 4Mb, and PC/104 connector. The small adapter is
connected to PC/104 bus, and we have a set with 16 different analog and
discrete modules on this bus; each contain up to 48 I/O channels. Each
module needs to be queryed every 10-20 milliseconds.

Ok 10-20 ms shouldn’t be a problem. You can have each IO module
driven by their own process. Each driver could put info in a queue
the server would read at its convenience, or store the data in share memory.
The drivers could use a proxy to inform the server data is waiting to
be processed.

You could have another process to provide TCP/IP services (typicaly at lower
priority )
This process would talk to the server via SRR.

I want to use a threads, one for each module, but as you tell, it’s not
too
safely… I want have a just one server process, because of better
coordination at all… And I need to have a link with user applications
throw TCP/IP - SCADAs, monitoring software etc.

Ruslan.

These client do they run on QNX4 or other OS?
On QNX4.
Ha, so client and server runs on QNX4, thus it’s much simpler to use
QNX4 native IPC (S/R/R) for both!
Oh, Mario, sorry. I misunderstood you. Clients run out from QNX box, and

have a link with server throw the TCP protocol.

Ok 10-20 ms shouldn’t be a problem. You can have each IO module
driven by their own process. Each driver could put info in a queue
the server would read at its convenience, or store the data in share
memory.
The drivers could use a proxy to inform the server data is waiting to
be processed.
Oh > :slight_smile: > I forget about shared memory > :slight_smile:> . Need to try.



You could have another process to provide TCP/IP services (typicaly at
lower
priority )
This process would talk to the server via SRR.
I will try. Mario, thank you very much !

Ruslan

“Ruslan Satlykov” <ruslics@yahoo.com> wrote in message
news:8rdfbh$9ma$1@inn.qnx.com

These client do they run on QNX4 or other OS?
On QNX4.
Ha, so client and server runs on QNX4, thus it’s much simpler to use
QNX4 native IPC (S/R/R) for both!

Oh, Mario, sorry. I misunderstood you. Clients run out from QNX box, and
have a link with server throw the TCP protocol.

But that’s my point, if both the client and server are running QNX4, why
use TCP?

Ok 10-20 ms shouldn’t be a problem. You can have each IO module
driven by their own process. Each driver could put info in a queue
the server would read at its convenience, or store the data in share
memory.
The drivers could use a proxy to inform the server data is waiting to
be processed.

Oh > :slight_smile: > I forget about shared memory > :slight_smile:> . Need to try.

You could have another process to provide TCP/IP services (typicaly at
lower
priority )
This process would talk to the server via SRR.

I will try. Mario, thank you very much !

My pleasure.

Ruslan

Ruslan Satlykov <ruslan@tecon.ru> wrote:

Hello Mario,



What kind of job does each “thread” does. The design you
choose is not really suited for QNX4. Give more details about
the work that needs to be done so we can help you better.

Well. I have a one listener (called Server), and some piece of processes,
which I use for gathering some kind of data from devices (I/O modules).
Server have two sides:

  • client side (TCP/IP service, for user applications - OPC servers, etc.)
  • data side (Send/Receive/Reply service from data-gather processes).

I can have a very many processes, with very high data update period. So, I
can’t say, how many requests the Server should process. And I want to select
a best way for this > :slight_smile:> .

Some notes:

– don’t use threads under QNX4. (Well, I can think of one place threads
might be useful, to have an irq handler thread in a process that also handles
messages. But don’t use threads for anything else.)

Have you thought about using Reply-driven agents?

It sounds like you have multiple clients…
making multiple requests, each of which could take a while to satisfy…
and you don’t want to delay a later request (that might be short and
higher priority) for a longer earlier request…


So, architect your server as a process that just receives requests from
clients, and then dispatches an agent … agents block waiting for work
by sending to the server… and the server dispatches the agent by replying
to it… and the agent tells the server it is done by sending the results
to the server… which then replies to the client.

It isn’t as complicated as the above sounds…

Here is some sample code…

(about 300 lines worth)

----------------------agent.h----------------------------------------------
#include <sys/sys_msg.h>

#define SERV_NAME “/dag/serv”

#define _AGENT_MSG 0xfffd
#define _AGENT_MSG_SUBTYPE_AVAIL 0
#define _AGENT_MSG_SUBTYPE_DATA 1

#define _CLIENT_MSG 0xfffc
#define _CLIENT_MSG_SUBTYPE_REQ1 0
#define _CLIENT_MSG_SUBTYPE_REQ2 1

#define _SERV_REQ 0xfffb
#define _SERV_REQ_SUBTYPE_SLEEP 0


typedef struct {
struct _sysmsg_hdr hdr;
char data[100];
} client_msg_t;

typedef struct {
struct _sysmsg_hdr_reply hdr;
char data[100];
} client_reply_t;

typedef struct {
struct _sysmsg_hdr hdr;
int result;
char data[100];
} agent_msg_t;

typedef struct {
struct _sysmsg_hdr hdr;
int howlong;
} serv_req_t;

typedef union {
struct _sysmsg_hdr hdr;
client_msg_t cl;
agent_msg_t ag;
} my_msg_t;

typedef union {
struct _sysmsg_hdr_reply hdr;
serv_req_t ag_req;
client_reply_t cl;
} reply_t;

#define EMPTY 0
#define AGENT_AVAIL 1
#define AGENT_WORKING 2

typedef struct {
pid_t agent;
pid_t client_waiting;
int status;
} agent_info_t;

#define MAX_AGENTS 10

----------------------serv.c----------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#include <sys/kernel.h>
#include <sys/proxy.h>
#include <sys/name.h>

#include “agent.h”

agent_info_t agents[MAX_AGENTS];

reply_t reply;

pid_t find_client(pid_t agent )
{
int i;

for (i=0; i< MAX_AGENTS; i++ )
{
if (agents_.agent == agent)
{
agents.status = AGENT_AVAIL;
return agents.client_waiting;
}
}
return -1;
}

pid_t find_agent( pid_t client )
{
int i;
for (i=0; i< MAX_AGENTS; i++ )
{
if (agents.status == AGENT_AVAIL)
{
agents.status = AGENT_WORKING;
agents.client_waiting = client;
return agents.agent;
}
}
return -1;
}

void mark_avail( pid_t agent )
{
int i;

/* do we already have this agent /
for (i=0; i< MAX_AGENTS; i++ )
{
if (agents.agent == agent)
{
agents.status = AGENT_AVAIL;
return;
}
}

/
add agent to new slot in table */
for (i=0; i< MAX_AGENTS; i++ )
{
if (agents.status == EMPTY)
{
agents[i].status = AGENT_AVAIL;
agents[i].agent = agent;
return;
}
}
}

void main()
{
pid_t pid;
my_msg_t msg;
int res;
pid_t found_pid;


res = qnx_name_attach( 0, SERV_NAME );
if (res == -1)
{
printf(“server couldn’t attach name, errno %d\n”, errno );
exit (1);
}

while (1)
{
pid = Receive( 0, &msg, sizeof(msg) );

switch (msg.hdr.type)
{
case _AGENT_MSG:
switch (msg.hdr.subtype)
{
case _AGENT_MSG_SUBTYPE_AVAIL:
mark_avail( pid );
break;
case _AGENT_MSG_SUBTYPE_DATA:
found_pid = find_client( pid );
if (found_pid == -1)
mark_avail( pid );
else
{
reply.hdr.status = EOK;
Reply( found_pid, &reply, sizeof(reply) );
}
break;
default:
printf(“unexpected agent request\n”);
reply.hdr.status = ENOSYS;
Reply( pid, &reply, sizeof(reply) );
break;
}
break;
case _CLIENT_MSG:
switch (msg.hdr.subtype)
{
case _CLIENT_MSG_SUBTYPE_REQ1:
found_pid = find_agent( pid );
if (found_pid == -1)
{
reply.hdr.status = EAGAIN;
Reply( pid, &reply, sizeof(reply) );
} else
{
reply.ag_req.hdr.type = _SERV_REQ;
reply.ag_req.hdr.subtype = _SERV_REQ_SUBTYPE_SLEEP;
reply.ag_req.howlong = 1;
Reply( found_pid, &reply, sizeof(reply) );
}
break;
case _CLIENT_MSG_SUBTYPE_REQ2:
found_pid = find_agent( pid );
if (found_pid == -1)
{
reply.hdr.status = EAGAIN;
Reply( pid, &reply, sizeof(reply) );
} else
{
reply.ag_req.hdr.type = _SERV_REQ;
reply.ag_req.hdr.subtype = _SERV_REQ_SUBTYPE_SLEEP;
reply.ag_req.howlong = 5;
Reply( found_pid, &reply, sizeof(reply) );
}
break;
default:
printf(“unexpected client request\n”);
reply.hdr.status = ENOSYS;
Reply( pid, &reply, sizeof(reply) );
break;
}
break;
default:
printf(“unexpected message\n”);
reply.hdr.status = ENOSYS;
Reply( pid, &reply, sizeof(reply) );
break;
}
}
}
----------------------agent.c----------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#include <sys/kernel.h>
#include <sys/proxy.h>
#include <sys/name.h>

#include “agent.h”

void main()
{
pid_t serv;
agent_msg_t ag;
serv_req_t req;
int time_left;

serv = qnx_name_locate( 0, SERV_NAME, 0, NULL );

if (serv == -1)
{
printf(“couldn’t find server, errno %d\n”, errno );
exit (1);
}

ag.hdr.type = _AGENT_MSG;
ag.hdr.subtype = _AGENT_MSG_SUBTYPE_AVAIL;

while (1)
{
ag.hdr.type = _AGENT_MSG;
Send( serv, &ag, &req, sizeof(ag), sizeof(req) );
if (req.hdr.type != _SERV_REQ )
{
printf(“unkown request type\n”);
ag.hdr.subtype = _AGENT_MSG_SUBTYPE_AVAIL;
} else
{
switch (req.hdr.subtype)
{
case _SERV_REQ_SUBTYPE_SLEEP:
time_left = sleep( req.howlong );
ag.hdr.subtype = _AGENT_MSG_SUBTYPE_DATA;
ag.result = req.howlong-time_left;
break;
default:
printf(“unknown subtype\n”);
ag.hdr.subtype = _AGENT_MSG_SUBTYPE_AVAIL;
break;
}
}
}
}

----------------------client.c----------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#include <sys/kernel.h>
#include <sys/proxy.h>
#include <sys/name.h>

#include “agent.h”

client_msg_t msg;
client_reply_t rep;

void main(int argc, char **argv)
{
pid_t serv_pid;
int ret;

serv_pid = qnx_name_locate( 0, SERV_NAME, 0, NULL );

if (serv_pid == -1)
{
printf(“couldn’t find server, errno %d\n”, errno );
exit (1);
}

msg.hdr.type = _CLIENT_MSG;
if ( argc > 1 )
msg.hdr.subtype = _CLIENT_MSG_SUBTYPE_REQ2;
else
msg.hdr.subtype = CLIENT_MSG_SUBTYPE_REQ1;

ret = Send( serv_pid, &msg, &rep, sizeof(msg), sizeof(rep) );
printf(“Send returned %d, errno %d, status: %d\n”, ret, errno,
rep.hdr.status );

}
----------------------done----------------------------------------------

Of course, this example doesn’t do anything particularly useful – it
just illustrates the architecture I’m talking about.

-David

I would prefer to use notion “slave” instead “agent” in this case. I know the
“agent” notion is used for everything sometimes, but anyway it shoud be something
autonomous. Under QNX something like

void main (void)
{
// initialization

for (;:wink: {

// sleep or receive of proxy (timer or requested trigger)

Send(…); // perception
// …
Send(…); // perception

// various agent body (reactive, planning, state space search, …)

Send(…); // action
// …
Send(…); // action

}

}

David Gibbs wrote:

Ruslan Satlykov <> ruslan@tecon.ru> > wrote:
Hello Mario,

What kind of job does each “thread” does. The design you
choose is not really suited for QNX4. Give more details about
the work that needs to be done so we can help you better.

Well. I have a one listener (called Server), and some piece of processes,
which I use for gathering some kind of data from devices (I/O modules).
Server have two sides:

  • client side (TCP/IP service, for user applications - OPC servers, etc.)
  • data side (Send/Receive/Reply service from data-gather processes).

I can have a very many processes, with very high data update period. So, I
can’t say, how many requests the Server should process. And I want to select
a best way for this > :slight_smile:> .

Some notes:

– don’t use threads under QNX4. (Well, I can think of one place threads
might be useful, to have an irq handler thread in a process that also handles
messages. But don’t use threads for anything else.)

Have you thought about using Reply-driven agents?

It sounds like you have multiple clients…
making multiple requests, each of which could take a while to satisfy…
and you don’t want to delay a later request (that might be short and
higher priority) for a longer earlier request…

So, architect your server as a process that just receives requests from
clients, and then dispatches an agent … agents block waiting for work
by sending to the server… and the server dispatches the agent by replying
to it… and the agent tells the server it is done by sending the results
to the server… which then replies to the client.

It isn’t as complicated as the above sounds…

Here is some sample code…

(about 300 lines worth)

----------------------agent.h----------------------------------------------
#include <sys/sys_msg.h

#define SERV_NAME “/dag/serv”

#define _AGENT_MSG 0xfffd
#define _AGENT_MSG_SUBTYPE_AVAIL 0
#define _AGENT_MSG_SUBTYPE_DATA 1

#define _CLIENT_MSG 0xfffc
#define _CLIENT_MSG_SUBTYPE_REQ1 0
#define _CLIENT_MSG_SUBTYPE_REQ2 1

#define _SERV_REQ 0xfffb
#define _SERV_REQ_SUBTYPE_SLEEP 0

typedef struct {
struct _sysmsg_hdr hdr;
char data[100];
} client_msg_t;

typedef struct {
struct _sysmsg_hdr_reply hdr;
char data[100];
} client_reply_t;

typedef struct {
struct _sysmsg_hdr hdr;
int result;
char data[100];
} agent_msg_t;

typedef struct {
struct _sysmsg_hdr hdr;
int howlong;
} serv_req_t;

typedef union {
struct _sysmsg_hdr hdr;
client_msg_t cl;
agent_msg_t ag;
} my_msg_t;

typedef union {
struct _sysmsg_hdr_reply hdr;
serv_req_t ag_req;
client_reply_t cl;
} reply_t;

#define EMPTY 0
#define AGENT_AVAIL 1
#define AGENT_WORKING 2

typedef struct {
pid_t agent;
pid_t client_waiting;
int status;
} agent_info_t;

#define MAX_AGENTS 10

----------------------serv.c----------------------------------------------

#include <stdio.h
#include <stdlib.h
#include <unistd.h
#include <errno.h

#include <sys/kernel.h
#include <sys/proxy.h
#include <sys/name.h

#include “agent.h”

agent_info_t agents[MAX_AGENTS];

reply_t reply;

pid_t find_client(pid_t agent )
{
int i;

for (i=0; i< MAX_AGENTS; i++ )
{
if (agents> _.agent == agent)
{
agents> .status = AGENT_AVAIL;
return agents> .client_waiting;
}
}
return -1;
}

pid_t find_agent( pid_t client )
{
int i;
for (i=0; i< MAX_AGENTS; i++ )
{
if (agents> .status == AGENT_AVAIL)
{
agents> .status = AGENT_WORKING;
agents> .client_waiting = client;
return agents> .agent;
}
}
return -1;
}

void mark_avail( pid_t agent )
{
int i;

/* do we already have this agent /
for (i=0; i< MAX_AGENTS; i++ )
{
if (agents> .agent == agent)
{
agents> .status = AGENT_AVAIL;
return;
}
}

/
add agent to new slot in table */
for (i=0; i< MAX_AGENTS; i++ )
{
if (agents> .status == EMPTY)
{
agents[i].status = AGENT_AVAIL;
agents[i].agent = agent;
return;
}
}
}

void main()
{
pid_t pid;
my_msg_t msg;
int res;
pid_t found_pid;

res = qnx_name_attach( 0, SERV_NAME );
if (res == -1)
{
printf(“server couldn’t attach name, errno %d\n”, errno );
exit (1);
}

while (1)
{
pid = Receive( 0, &msg, sizeof(msg) );

switch (msg.hdr.type)
{
case _AGENT_MSG:
switch (msg.hdr.subtype)
{
case _AGENT_MSG_SUBTYPE_AVAIL:
mark_avail( pid );
break;
case _AGENT_MSG_SUBTYPE_DATA:
found_pid = find_client( pid );
if (found_pid == -1)
mark_avail( pid );
else
{
reply.hdr.status = EOK;
Reply( found_pid, &reply, sizeof(reply) );
}
break;
default:
printf(“unexpected agent request\n”);
reply.hdr.status = ENOSYS;
Reply( pid, &reply, sizeof(reply) );
break;
}
break;
case _CLIENT_MSG:
switch (msg.hdr.subtype)
{
case _CLIENT_MSG_SUBTYPE_REQ1:
found_pid = find_agent( pid );
if (found_pid == -1)
{
reply.hdr.status = EAGAIN;
Reply( pid, &reply, sizeof(reply) );
} else
{
reply.ag_req.hdr.type = _SERV_REQ;
reply.ag_req.hdr.subtype = _SERV_REQ_SUBTYPE_SLEEP;
reply.ag_req.howlong = 1;
Reply( found_pid, &reply, sizeof(reply) );
}
break;
case _CLIENT_MSG_SUBTYPE_REQ2:
found_pid = find_agent( pid );
if (found_pid == -1)
{
reply.hdr.status = EAGAIN;
Reply( pid, &reply, sizeof(reply) );
} else
{
reply.ag_req.hdr.type = _SERV_REQ;
reply.ag_req.hdr.subtype = _SERV_REQ_SUBTYPE_SLEEP;
reply.ag_req.howlong = 5;
Reply( found_pid, &reply, sizeof(reply) );
}
break;
default:
printf(“unexpected client request\n”);
reply.hdr.status = ENOSYS;
Reply( pid, &reply, sizeof(reply) );
break;
}
break;
default:
printf(“unexpected message\n”);
reply.hdr.status = ENOSYS;
Reply( pid, &reply, sizeof(reply) );
break;
}
}
}
----------------------agent.c----------------------------------------------
#include <stdio.h
#include <stdlib.h
#include <unistd.h
#include <errno.h

#include <sys/kernel.h
#include <sys/proxy.h
#include <sys/name.h

#include “agent.h”

void main()
{
pid_t serv;
agent_msg_t ag;
serv_req_t req;
int time_left;

serv = qnx_name_locate( 0, SERV_NAME, 0, NULL );

if (serv == -1)
{
printf(“couldn’t find server, errno %d\n”, errno );
exit (1);
}

ag.hdr.type = _AGENT_MSG;
ag.hdr.subtype = _AGENT_MSG_SUBTYPE_AVAIL;

while (1)
{
ag.hdr.type = _AGENT_MSG;
Send( serv, &ag, &req, sizeof(ag), sizeof(req) );
if (req.hdr.type != _SERV_REQ )
{
printf(“unkown request type\n”);
ag.hdr.subtype = _AGENT_MSG_SUBTYPE_AVAIL;
} else
{
switch (req.hdr.subtype)
{
case _SERV_REQ_SUBTYPE_SLEEP:
time_left = sleep( req.howlong );
ag.hdr.subtype = _AGENT_MSG_SUBTYPE_DATA;
ag.result = req.howlong-time_left;
break;
default:
printf(“unknown subtype\n”);
ag.hdr.subtype = _AGENT_MSG_SUBTYPE_AVAIL;
break;
}
}
}
}

----------------------client.c----------------------------------------------
#include <stdio.h
#include <stdlib.h
#include <unistd.h
#include <errno.h

#include <sys/kernel.h
#include <sys/proxy.h
#include <sys/name.h

#include “agent.h”

client_msg_t msg;
client_reply_t rep;

void main(int argc, char **argv)
{
pid_t serv_pid;
int ret;

serv_pid = qnx_name_locate( 0, SERV_NAME, 0, NULL );

if (serv_pid == -1)
{
printf(“couldn’t find server, errno %d\n”, errno );
exit (1);
}

msg.hdr.type = _CLIENT_MSG;
if ( argc > 1 )
msg.hdr.subtype = _CLIENT_MSG_SUBTYPE_REQ2;
else
msg.hdr.subtype = CLIENT_MSG_SUBTYPE_REQ1;

ret = Send( serv_pid, &msg, &rep, sizeof(msg), sizeof(rep) );
printf(“Send returned %d, errno %d, status: %d\n”, ret, errno,
rep.hdr.status );

}
----------------------done----------------------------------------------

Of course, this example doesn’t do anything particularly useful – it
just illustrates the architecture I’m talking about.

-David

there were many interesting oppinions about how to distribute data
handling inside single server so i let myself to send & my a little example
:slight_smile: seems like it is more closely to your base request.


Ian Zagorskih
Novosoft CyBearNet Department
Custom software development and web design since 1992
E-mail: ianzag@novosoft.ru
Phone: +7 (3832) 39-72-60, 39-72-61
Fax: +7 (3832) 39-63-58
For more visit www.novosoft-us.ru

“Ruslan Satlykov” <ruslics@yahoo.com> wrote in message
news:8r4jk3$n6m$1@inn.qnx.com

Hi folks!

At first, I’m very glad to see this online conference!

And my question is next. Assume I have two programs - Prg1 and Prg2. Prg2
is
running in foreground mode, and “Receive” a messages in circle:

pid_t from = 0;

/* some code /

1> while(1) {
2> if((from = Receive(0, msgbuf, sizeof(msgbuf))) {
3> /
prepare reply buffer /
4> …
5> Reply(from, replybuffer, strlen(replybuffer));
6> /
some code */
7> …
8> }

Prg1 issues a Send-calls to Prg2. Assume, I have a very many processes
like
Prg1, and only one listener Prg2. I had tryed to insert “fork” call in the
Prg2 between lines <2> and <3>, end “exit”, between lines <7> and <8>.
But,
when Prg1 call “Send” after first successfull “Send” call, it return a
error
message:
No such process.
Can I use a fork() to speed up listener, and process each Send-call
individualy?

Thank you, and sorry for my English.
Ruslan.
\

begin 666 mcsmodel.tar.gz
M’XL("$M4\CD``VUC<VUO9&5L+G1A<@#M?0U@%$66?T^F9S(S3,@B48%;+,# MFVB^)A @!-B0$"6"D \R0$#'&";,2#*),SU$/G8O.K WE4Z4O=O3^[O+K2RX MXJ[G<G>N!MC5D$02T%TCYP=_]#S!K'0NL(OH0426N?>JNV<FDR]PA5WO:,QT M]:M7KZI>5[UZU?U^;66UT^[BF:MZ9&1DS)R9Q>$9CLBS)6M&9A:7,=,R<_KT MK,R,F=.X#$MF5E8FY%_=9DF'U\-7N*$IS@K7QHJUP_.Y:VM'5%-DY[XQQRSX MVZ::S#RJPBL5_:<<>]3PTP!_)OA[.E3F^ T,U\HR3!2SF&5D?M4H!_+P=@^? M[K&[u]O=3(';7>OFJIRN-4[76DXFPE%*4UR=<PWG]'!3UAB )O%Z[!)O3873 M)1?@:NP>3\5:>Y#'Z5I?40U%/=45Z^T*$\@R,*41%%DTU.=:P[GM#WFA;1Q? M.Z#DX+H'R U6/D#VF@J^@O+;L0X075>]P2Y5-YMC:)V&VV^_G5M2MG@Q5^'Q M.->Z:F .<FOLO+V2MV.KEM3RG-U5ZUWK@#IJ:MT;@%;F6N>JK7=Q=MJ>)"89 MY-Q975O!0[-2ZVJ=(,'CK:NK=?.<"XI##C0@QC!8%G:RHKJZMK*"MX/^JZ'A MO-M;R7O==D^,8<#84#$C'5$C<D3)?]>/OZ[C^8ZO5N[=9QGF/?GO0;C^=3O# MY.UFF+LN\P]-B'_W8+ET!'&_?@Y_I_T\FM%"0GCJ/_Y9\W04<_(&A>NWS#8X M0J5*BZW+RWRGC/O,K0QC.61YW](JW@RBR$%??\ ;UY(!9#$1""THO_<<_!0Y M%@+1\<D!AFE>;8X3-T*.K\-H.6(Y]"!+SI!E9J/XMT@#0DMJU’ <486'SW MVNY;N:I=6&W6;7W?RSD:%%EBIBK(I ORE3J>#C*,DRH2>#-K=;0^56<931O
M+YA/IGF9V22>DFJQG"8?/@"\Q&LMFL(]5F5OR,4=IT%-MTS^ VL>8B89E9
M5^X&QS.V4P65J#7('IYJ;%9K:J<X$95.QX"1@"J<^WHRI+?>22)OE?5$#
MS1$8\08XD0-;C_!)#;,9_ELDSDRZQ;5(7&!FH;S.#%7 V8CS>S_^$.,'I$M<
M0%D^8/,[V0^^35D^8+&E^3OBVB#GA#B’Q58EE:."H%V>8$Y@7P,;8\CK!DZ
M;1(+
=;OJK=NMQW
H%TDFZHWG
D"FZ/MNE8,V\V8I(EBT$[JT%+1\G’ED–
MQZJ ,!XDF’^EPGP3M#2.7/"?VL/T__:L1R-NIG(3;/>UL<S7)'F,)#D1)/?M
MH:Q2 X,%/FB2A((0X9\3 [O44D%/E73>[>R_7+1$EE?G,E9=12&:4P?X:
MR4’R’CEJ.4(FFN5BY&.E(/;X"W)V#RL5+)(VNZ[)(:J>0-4+*O(Z]P<>G%
MQS4XMA[7[(%?,J45?^/19)!)+?@;<S^8(=4D
%U!3C2O?I3MA2’"+)^_+’_I
M/5Q^>OX==TS+Y$J\KM1ESAI8H39X>'M-&I=4F<SEU]9M<#O7.GCN@0V<7
#0
MQ=O=+E@+:UT5U<#AKDOC±FSLE,MV=E9:=S\ZFJ.%O’ <DR7Z35I44^#2:AJ
M.O+8L8OS7F5@XCS6-F\O!^?D+][JK6I1J3ZK^N6
V"W%,+Q)V[R6%)Q5?P<7
MO=^!G_:2?3BP6Q8C=3,0RML#\3W0L=?!+FG+R.]1;YVD["+Y<.>R#NS[“OAM
M+OA\7RS.EH+^YH)^,FD-T,0QH±]/]V%N?VDK’?6& 0SX’(IJY],9"NDKCW
M/PXLXH=1\N0GDSZ"VL3;X’K’*4CY6M7FBY8CR1WDN# )“3 =16QZEU!V4=1”
M’>2SE@4W,MP4*X7364,ARWKGQPMOG A$! 3D>G4O=%@+=IQ<DI-_4A,!SJ8 M&)R5[6 KK.3 _ES(V>? UM^AEHQ3WK[[014ME)8!-!'=2G)P/UHZ&#I=XG- M?.0@YI,V<D8T8J_K@D5^II93]Z%M*%Y..K5-1Q]K33Y4:&D]^\+YMPJC29=M M9:0->R!HPYA2TDZZA0GB.IB-@7S3*KP/9>2 D&L*Q/<#@[#!V%AA%![2-?2\ MZ3N9X:\P;CWG!:%B%Q1HG&^TG-O-\C'^CH:>+U]!_0)#4]>40 "7#>YG5)WQ M&FS[UG.\J7&EL>%UEG3Y5QK%!* *\Z&6BU#+N?E&CM?L"\ A.(WZ/\$\=5+S MVUXD/G<I$'B<:<&\R'[L#K/%5M \3/!W&U5^UCQ_2RL_83^]WR=:T,Z*3JBN M[V-0X%Z0[>$KC7%T/FWFHZ)=LC<VNK5D/DJ/@OP "93#O8N](2+8SA5^:
M@N>]FH?WX$M
=8E3US#O"C>,.M.5;V&?-9XI\IW4-LG9G)CQ?RH_2MO’9R
M=$F@N_R@.JJO+;I%34(I5K_NWZ0E4=E+8R4-7N0K!MFG:F/&23OE6@4ITPC
M’J=1%C2X/%(U$U1!U920-CK4FR>:”!WBL$@$8&B8O^[!T[H&?I#_G_H@#W
M=>4
F)6?T]E1U;[C3#O.6U0O:^[,I0N3J,.[!\M;7M4^DS3:-^.]8\T[D5LJ
M+$AVRT0G*Q;!(2:H=_[<#J!VR'"C05][_D2FK^JO7<WG",Z,3[8B8B,X\$; M7US4:/#K&KY@^'&-=ZM\;5'^A2I?:Q00O)^L6-D.6^)IT[-FS)R57?% Y1I[ MU5J'\\%UU36NVKJ'W![>N[[^X0T;&<DE2B+OD/\D[V8<:5YL5OGCS.#KF,&7 M@.7B_#2B3FM\- ##S:_"%6T^#):/"F&D:>_<TNH]1T18F^EH+29O^;XT> U; M6[^K/7_T8%1JGO@3Z!7I@BSK<O(N3,_)T<OO.%2XASG[PM)&K3]Z#@/3F]Z
ME/\NE:\S:@[C%6W1I!^E@=%HG&("][0L]]_PB\E#[-[N^"N^^/QJB^O18>W
MX^_0!+5#JVZ5KA]5KF.E<?
/.$YB_A0([$ OEBQBR21,^ Y,)FJAV+CUB/=E
M$E$%+0B<&O0^O1.@GO<HJ8^(QBOM@,?L<G=KV#=JE;+^ZJ#5%3?‘8).R&5I
M+>=R8W*]$^!7YXW)/N…G9L(+?1$68Z(&2 IGW1!>]XB1ZFEHFXH:1?R9#-S
M+L\8Y9W<HL?&/P/<#7<;.9J)UH’.;YEOHTZ!DQ/-7DJ .(NM;L7< @?XQ)
MB/(G")EW"AM9DGF7*"1;&2W]GHU.1OCO&.@%+C?.,F?“XJUM/J/2BY>,4P1
ML1BJ]6V,8[Q3&Q\PGNO(Y?6=>7’?AT)]><Z6’J%O>R+A@2#EY>G’@SKA1Y
M.A#±=YME#D:CN]OSDOPWVWT_Q;R&G[+^A\T]K’0(YV0I^O,PX8S)I\D%7^
M?;AG.= -U;$6$Z?ZS#Q2_TG&T[^$>RN?T/,[JC8+?AH15@)GG7L%B>_4_A
M(18L[?U;3WM7D_(!;"PXK/H7;;A8C,&3’G#&QQY!ZRTZKWD=_O>]FW0,7QF
M0X7QC^?FQW#>)&$^N[65’T?:J/$?PS+_Y
5EJHQU’QSY#4PW)‘5%!9N=
MVAU)H40$=98/=IF+@AUKF)N$NRC^YD=.X!)14O1&GW:YM7!!P5)E(P"C+V2<
MX- ?XE-FF7:K>;W?E’O@L*[0^UGQ@=XH\1;(6^D[KN;’^DV%D%FGN35_JH
M]FWT@&)K=]
ZXBI.9<M]'Z.9?C9A7PR,9$BMI 4Z4B1D4^85<21(J#$26! M%$ULSN6\_]&<>Z8YUT09)3ZXHE7%2]+X&/FLH>?(WB8,O(W:V*=C=Y>A]3P( MFM7X^M]>'VUI[05_B?E-"OHVWU-G]%B.^,WF*M_)BX%X$_4M? &C1]-B!*8< MS72@K$\08C+@O/60]TX2@Q0A!DOWS;(<ZOL#>)8BH?F27LGKL-"-5]-EM]6[ M4)A$"\R/RSZZ7K.3I@MU(*FK88Z9@;EQ1IPI+8#QD'1H,<E/R#CD-YI[W3#S M#K)F5:\3$AFG_:E[.O!N)V CN\&PWQO[7.R.:)L\IZ&[@7@=RL]"8P(#\29A M"0LNA*!]Y*0:G)^=2*[RO:GNNXM:L3X.5GZU]T;):KV!"P)JJ>Q5-#M]WP+[ M],@7NT&3[J,M4X'R*LH0GU'8TIH7ZO9/QF'?!48F\PL@;S!*%FX)2[)H0AN( M-^*YJV\\5:=X%ZXIE&9I#;:;M.]#;B$*I$X1%K+9K_,L:>O[8V.AL>&0VK_( M*"20FP5J(6D7B D+ED@4V.^-Q]J/#5SDAGM^WVYO.“A-FB30+UCI7JI.8,5
M+V<%R[,”$ID:D-A/<2N0RXX9B
ZL>#U’157XB+2K9C-^0=W\-&L(628:QI MO#_&<@ZM@QZ\K=8O43O4L^J7W-EO:^C\Y<>2-F&#R?(^Z1<_0%MXFC?@]%X) MV?J#<!]T?"Q,:_%!F-&=\_&&A/PSFF"QXV<DB]ER&V2CU1SSWU"=J;R])1'O MV.D6?/ @?@S-DAPVU&T;+ F<%\VYJ*7CC(\#<\TU;C2>ZUS@U?F_:#B4"]:2 M.K#0O2+8-7UH.0UN)DO$OM1]S*7 WY SR=TW';JM2]4-G6\'L\::Q2>AHUM; M-QM5KV5W>=]1O9G\N[[7<43BRADQ)6\-^;O6Y<63'SL&/E6!KW6,.]?<3_V7 MY.[_EL3GY,?5WR%XC,(F4W9_O4;P)"4'A!NRN]??G/P>LKRI>J^YE!/6Z[+? M]9XDF^*26_L6);>13:9 ?H*P1.>[8')/)N=5G623D=Q(M,)]++&Q9*F.;-+! MI-S$]L4'%DTD9\B;PGJ6?$]'ZEGB@8VB;ZR*G/&ULK>N!.>N5ZL*:^>GD/L' M5M#N5O&?F"^A[//1.%*\M^9L,GK54%P=3;2E@HT5_H968V-701U]FZ'V34;P M+\?FU+/U+F%34O8E]]J^6.""Q'W-F[@<C[G>FGQ$@)8OFCC__*>%V5V>*4)I M0O)A56OVX?JHY%9R7CU/N/%<FXI/\UT(\+J&$Y=4[RWTGI($JR69$R2)1NFD M@5-?K["<%<;^[072KX[./N,UJ6-);(X'6PMJN=0W+I _D5AUY'LLJ=?!W:YG MXL2(-^CRUED6J\FBTPYB^+P’$?U01U8-’:6]OTV6UN/?@-N;CF?D;65Q]
MWP=+7
KKTY+X%#K1>#68[N9R3HCRM:5TYGV.0[DSKT<ZG9).9^@J7-Y/RB_Z
MQ!12_A&)ZF2CG2>X>R#N9TBS>V;A#RS8%*U"70#[FMCLX]Y-0).44MKN63U MRHI\<V?APN9%T_X>KF1SD?D2?P8\XV[J>,1N>96A/K,0@UFJP]F'/9I]EVB% M8EJ4-$5A</OZ^^OW9;>M?PF&%3BA>1>SN^M_+C4&_.^^\<*#.IBYL=G'ZMGL M/G>T4*(#[^V_H)U3A:CF11=5W=D'O+<(T\V^@RR9;E9E"BEFTMYL3.I446-Q MJV^.>5;LHV62MT[4Y"@Y)CZ%%K5=P J :%01=2 OH3F?!8<&"XG+0X_.V'LE MFPD>8;M@\K6-:3C1#PW06EKOV6('2 )&C[!US8G9X+[OX0LW"IGG_5HR%D8
M/&3"UB.Q
] Z!3<]U$%2NI#:QZ[90-U)$QT;7N1KG6\/OO3>DW.0SKWS?)R M-P8(;/:';@.L8-3O\?ZA!3.:CH6M?*T7H7.IN!P2*BB[US-^!UWVBN- LS!. M\"*CQS\EM)HIW0(;F6L24W&;WXYW)"?<16W@8\[EQ9AX=$)OP8>3&UF&'TM) M&G4YVZ>^HYP-NI?D:/D@3TC/#+.AF<B$;=?:92W$XY@BDV:%+;D@4JUID7P+ MTIZC06?),U7<^L= H)/!]4U:7* KXC'<Z/8'%'TO;&]>&'N@EX5U7[,7'RI+ ME<C:(UW@WM/[C)[>!'%Z4*!4<]^STK"5GD+)K>G">G8B11’'498AA^Y0!<W
MN*%;LV!M$”?2S5ZD(M3!M(&8V.R*Y#//25+]!R&.W+C3RQGQ8/T#!XB>?
MPVT=#X/M$WE-/XKU:^B#[VM^5-+WOVF55[…C(R,&3.F#
?^-R,S:UJF\O[7
MDI69B>]9V;-O/[^]UH<Z;=?W2/=8/B6TU59[5UCY^9X^#75S@?2’/,&TIRU
MD22WT[5V(,WK<@(GTL(9-WC2^0UU=D]$>2"OL[M=]NK!=%=%C7V@E,3VIJ:
M6E>:(]%@N ;:P/>T^ (["1,5[K65
5REH±W0
:ON3>8V&?1USC4V7B^]
M3H9T3I""KYDEI36Z_$E<PI]OYQC%(5EY04+,3-Y1YR/6S#MJD=[U)&2FE M!276@A+;DOGW%*3@Z^>4C.1D;NY<+M5"Z]77T9?+28E#O9M/3(9J]6X[[W6[
M] 4K"I?9[IQ?N+BLI #(WS7HJ^K@IO%527"7:KU\2F+D:S5KL248…2L;U*
M;Z"E&3E2_%=?@+4A.GGPI&2D>YT9[+=0A4T9J
C!J/T!!L2:ALT3A(? MV<&1XPV@NU>HK\'A"51E2E=18X-U/'SH@J1O%Q:DXT0_E[.@FNEPT4>J7%'Q M5#JDIE*FH+91BG)!<RY'[T,%2OPY*ADVJD+IJ#01J)[":R@MR\\O*"W-,7R7 M2[]=&@TX#:_^3/]+6_;+.^3UO_9JUC'*^C\M<]J,R/BOF3.OK__7Y&C(9#+2 M';4U]G1)>GUM>YUZ;6\P^Y.EUZZIM=4>FIJU]BKTQ5?L<,?S33:)IG:=VG
MF+JT0,7DK
//8AIZ3\99DZ]8W+
[4Z’H&->GW)1TK>=7E_!PY*<;LE(FT&O
MY84Z7?$>;OG/_9)Y[DS*/QHV?Q&A7O?,$WJGC)ZWB;\J_ZZ2NC\"L>R;_Y M,X"_8[>/2QI9?LA9N8=6\:=_>'*4*JJ=-4X>^._V6X!_W(Z_OS5YU"H4Q^?G MM,S$RRJ#YK/FX33'#_RW0YF*7_BXQ!'+.&?-2',\XT\'YGBHX-NC5B!Y78UT MC*PP7<D8D7VSY7XU%#4^,8EAV/RE"PK8!?.7S5?GE99J%]Q5LK2L2&-;5K!B MV3/_3\\X,1Q'&Z7:Z]? T%RJ>N:):$:3OW1)Z;+W(/<Y##>*5JN>>T+':"DU M<R<EPZ%#,C#;4/;O%:I>K=K]A)9A;5#932@?B096]<E3!D83B JHVQ <^.
MEZ58MV]<@&-UTTS&%.$HVIAH:4VW,5J;S>:L?8#1R6LS9-ELV$P;$[_C)2EV
M
QBA1=R!8FE5 L^K@:">&A6&;X=> S;QJA;F1H5B41L.>9@T0,#@4:[4B
M
YPI/!1K\P"&C=OO8GKT<:JH77%650^'B2G6J)XL2/3,MJIWY5O97<NMFIX*
MSF"G$<PY_N0(UC9GI_BQ;-P;R5_7"'GU’M8<BNW[(-%=6%THNDOIX.#H
MKE W7QRFF’HKJ,#NC$PNNLGVY.D3O5,@+;=!EW(@2X48!?NABY48<XZR
FS
MLC_XP5B&46EPD-GH&K!\1QD3]2_2>O!U1KM^V#2!T=DJ<8’EZ5#"[0[SG%/
M-/P$C/#T]SC
&,@75/!.Z;-FNG>1G/L-5ZX6-L(^DJQZ5?/#DPWLU:QC
M%/\O8WJ6XO]-SYIFL8#
-TWOYJ-4H[X[?-7G^4P76H8JSV?7WG//TB6V
MA;;")?F+RQ84++#9#-^"+
?+KA\Z-Y@=]N1"KT,6V83@0>M5=7U/=U7.6HJ
MUMD1W UZQA]_BOXG^G3,RS3Z?R?EG5]_E^+PV"0MF'ZN9R4,$C3"JYE#]%0 M45T]FS/+C^62(24Q)F-1]VR#WEW#I58-Q<#=GE8+?^!*&@QIE6FUP%M9R:7" M?TM=]@K^82ZU?AJ7NM'.F><8#,'RL^4'GU!6&9Q2N=JP.I2V!>N:K5>>9 Q1 M2FF/W$.#4D&PJDI.60<-BIB@P+"\O_3-N@I':%MR]8Y1\'\S,Z?/C'S^,R-C M^O7Y?VV.D?!_9S3,D/B_)V]B.$Z#^*[=5X3_JY31ILJHF^*9S2%]\+;9;5_K M]/!V=VCKS.$.E5'*<G17X:TS@ ?K<MDK,?)?V:%P5>[:&J[.75L)UW0G,F ? M$RS),%6U[G41.^FP4LIN"+E"NR&9@Z$;'Z5&V/D,JA2.-;#;KJVQ<_B$(RTM MC=+H#HFK=_(.:>^$K$J?(0<K@DVZI*E@951'[AJG"S%[(6SB-P%/B "ME-:! M8^XZKO!_W]&-X1T!#%ZY\F/CSQEFD_R'V$(,RS@!Z9.7^8?F*>D7@^5*V,(E MF,.]]@L)6_CCO[\WH'TZBME^D\(U"%N(( %AM=DDQ&U/:&.8$L?T5H9QS&NC M3ZO,XK]*>+X6)%#LVD0S^5C\C0K#/2 MP=>:&U!4<PDCO+\3PW,4PK)$Y8&9 M;M^<5@H@\&.43J?TL PABN*V(&YQ'OSNN_]IN*3HC]-PZ7 RU[,+;8<$8LH
M#$1’XK$.R^G]\A,GEHCTZ=+34?)SN-@MAR#’]X7
NZS(\3!(!;C:5&CZ%?+
M<=5*L;…GY,<]?D@3W]J)FBAPO4NIQ2C62.’.?K>5M;"N&H_65%N’#N%+’
M&<KT:\ID$A:864<<C;,O)@O,.G&7)+(%%Q7QJ(0J
G(D4<Q#1H5?;)TVAG: M%7*"G!%CI:@RKZ%E#L;_H[>20P-X8[<8L0_0*3VY(K40A$‘XI"Q5E27?B$
M3"<_!AP:<-BJ40"'O;_2#(IGPF=R@52,YPE#24[44I1DJG8(E.3WM"&4)#\T
M2G
5-H22K!^ DJR64)EVA!$G%D<6:00N/342J>R<? 5B!%.!MHY!&HC3XJ
MI.>O%SIY7G.UH)/;-5<(G:S67#ET<M:5E)&ADZQ2YDJADZ^S7Q4Z^0.$D79$
M##]3V/!#7*‘OE!E$ON.;BS’,3Y&=+(86=\"5_RWH<Q$R<28)\)D0+SN3N3# MF5TI?@<OD1%Q=>3D,\\H8PE'* Z_'30N^CC<N2,4R$L?2F\]S<=@?&4!@[.9 M%+!2J'8<T,=V%E!KV5E AR;.-#,%#ZY"D)4.05;RJ(5)_)&8$\ P9UY-QZBN MG$Y'(VG'5F"HN.7(>=XL%Y()Y"B2G&VY>;%T2E$QGU^B8F[:CU3:IK8#%VX] M_Z4SSHRE5-V@_<'QBK\*ZE'&IF+R<0V-OILRD>(S:23@I#@:;-E%L:E=?_78 MU)=D;.IF-(OG$9NZ*PR;^F.D'I>QJ;D=0V)3?TW[WCH$-O4-A&76(DKSQ,\& M8E/+U8.PJ6\"BYBG#F)3$6@H;D5L*@($!V!3*6*P2URH5K"I59'8U'NP5S&; M%6SJ[_H#0D()F-3@UC/FB&PG@BV’1;K>7ATK.>>9VGS;AP2ZWFS@O5$,.P
M6,]P.RSFJ’!LV^CAG9J%/#L’D3"4MINS1#@V=NT")[%? J>W0(^UJ#129K
MY=17,_B1"BVDH-535VQ+[<6[4WX)8(CQ%N @Z)6,P+#HE83@@(B,O*'R[AS MN(S=X79N9 #L4R,!8)\8@;AG_]QP[$K/Y3Q]>!?_TG*FM7I*ROAG_MP*DZ M8WC\*]4,P@=P]E#\*R84_.L@["NZ3A3[6M0A8U\7=PS&OJ:"*1V(?6V0L:^+ M::1V$8UBIG9Q/:UH2.$?3TE85^+9.SKPB]I#‘3C%$<([>F0T9Y<&Z(]‘6%H
MSY?9@6C/9Y5K&>TIHE8XJ&$’[BTHVA,3]&>&!@?CO;,TPZ!]L2Z9;0G2A@1 M[;E107N6:4=">Z*A"4=['M:&H3U7=%P5M.>*CD%HS_7:*T=[?D?]M:,]90]Y MH!W^L]">;VC^/+3GVYK+0'NB0H=#>XY7#6.LCH<,Y[4#;:]XX1J#MG%U;^I^ M#-:15> $E10%_AW=MI9HW"^?C="(=3B[7A3,:)B[!8%%EX68_09@94>%Q/H[ M!D-BJSLB(;';.B1(;%.'HG=1F%#6#H<$HOY8<"@<6&06%I AL32= 0D=D8(
M$KMV9$ALPFLX)?B.X2&Q^+T2(8MBF@9#8I$L0V+1W%X.));IN#Q(["TR))8N
MPB)I0EM()Y^3B0"$NOHB(3$(O=HD%AL/>U"$!)+NZE 8M,I)!9)(4@L:H,V M28+$.CJ&A,0B^6N"Q/[H0A 2NTSRHJ>.#(E=$0&)Q=UB."1V18><P,_P#(+$ M9ET1)%;S-4)B7[I22"Q:F.N(V&\F(A9MW>B(6'_'UXB(]7=$(&)QVS\L(O:' MN'"%(6*1>1A$+&9=*2(6&_--1,3B!OV*$+'5=&F;^-H@1"Q=[89"Q&+&$(C8 M)OJ0!@511"Q=]11$+%X@(C:TF(V,B)7=^,M&Q%*/<5A$[! NT$0F; /7+FLA M_H?T:=,/.T(K+D7$QG4$$;'X5,HS5=SSE1"Q6(FLO4A$K"T,$8LU(R(V+OCL M2VZ-A(A%0A@B]M$0(G;<YZ,@8G4=Q&QU’.([^X8&A’;]ED($>OH^ LC8O]O
M’4HS-6L8Y3XK^E9(?R/A7X+VC(M(]-R/?[C6AS?//QOB&9WNUV119UK7161 ML%]^385[[=6&#L,PXIV5^O6UU7"NMNOM#SMY;$UMCI+'5=:Z/+R,]D5X"_1S MU;W<7&Y3HA3MD9B2*,>T?C?GFF"1Y7:MKW6NX6H\:RDD66H!-B]E0(.K:MTU M%7P*EY:61F&?ZRMLU7!+]/B3,QB[B9$]B2E*+T-"[T4(*)2E03A)6#A%DDSI M$5+D.I%++H9@5?E2PG-ZUG*1/9'&@,U1X5I3;7=+G<(;09L=O"V@=_GV4$$# M"UTCB.B5@,%#T&^)( V96!P1<<!0=L-]MK[0[u]LC.(23SA8O(+GRH=
M\'B(P%^AXG4&AGK:]!+"D\J+;RK<,FRE('JES#+2G<0N8SMQ1$:-CH3!X9S M28!K&(@8S!1^ES-RN+!+O,9>&((*D>'G(84"H43*B5,5=06Q#_3RV3:.]1@
M6+&0AO0PHJ2^<$HM4IB5P^ZVX^“26& NR1<PTWBGRXNWCFI’J$'.XNR%=&1 M:A@]_@T1TL%FTH:'8^\QNBU)H@Y OM\6UIWA,/%#-&>H4#L%#*_7#[RM&3(Q MO/?0?\Y>[;$/)W^DD+T(P#PM+X7;S>7")A#680A5%/'1@Z&"_:3FAS<LLEFC M1 -BRY21%'$KE ;*V;=GY@PI?V D85"CGFJ[O2XI*WG(0H-##:5V2#AY*!"< M&-(<"QM[)<@4&MP1GP08XBL@ZS”<+&,LBXE
S#D, ^:HC5VV11)5BIY*"L0
M&1:IV('KWP"XK$,)A;^:=8SJ_V=%O]?IF5,NQ[?4V.*'
WO%YX?%QOB
MKP#;K8A[\CK^?U3/9XM?5LTL@]EC=$653\L2=&$Z]LEAHH];G1]>0M)%Z
MX_KG""[
<P3O2I\CB!Z<P1+I,\1K+JLSQ’@BQN]6O7T@,\1X)MLZLZ,?!S M!/\FA<A_H_ 6.U\<P^AL\I:1^:!I\A!?'PAN$6U,V\N)#*-BP1^04.RQ<0
M-GSWS
A?C&$,MJ";PUQLFACVN0-IJQ/VN8-77AP_2 XSKLG 1,M$YOLO1C-2
MG3*?O@+%P<KTMHV*;R<0.S@OUO04(H7!RIPX2+8VJH<’$,N%^K2"'BYN9
M=]6H.'B]#)*R7!@H$DP7!PI9=OG,SU&JZIG/'X4X$9KU"[.JNY)@O,,*]M3 M;-7T./&S756;<]F3#QBC>[99M7M>LJJ[_D)$IZW&O8TQ8%>;;+/:F,T>&=M
M3.Z.?U4^B
C;2+@T+2:?,'A:0OPP8JJI)XPD+2’P[+#86D;PLO(X6D=.O8GGE(R(?N%0+A7FMLSP-(<%@-
M7-@W(^Y’B>=W/,M$_;/D0_QUHZ2R=HQE6,7=01-:L’T,T/@V&?AY[Z7QX$5
E"=D=/’[W@3H;+C=P./T]<]97#^N’]>/_TW’_P!%'V]J'8`````
end
_[/u]