home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of A1200
/
World_Of_A1200.iso
/
datafiles
/
text
/
c_manual
/
system
/
messages
/
messages.doc
< prev
next >
Wrap
Text File
|
1995-02-27
|
28KB
|
904 lines
4 MESSAGES
4.1 INTRODUCTION
The Amiga is a multi tasking machine, and thus there may be
several programs running at the same time. Although each task
is a separate process they can communicate with each other with
help of Exec. When a program (task) want to communicate with
another program, it creates a "message" that is posted at the
other program's "message port". When the other program has read
the message it can change it and "reply". The first program
will then receive the new message.
To be able to communicate with other tasks running at the same
time can be extremely useful. When you are using parts of the
operating system it is also sometimes necessary to use message.
The operating system will for example sometimes send you a
message when it has completed your request, or it may be want
to inform you about something.
4.2 EXEC'S MESSAGE SYSTEM
A message is actually simply a structure containing some
information Exec needs to handle the message, and your data
you want to send. To send the message you then simply give the
other program's message port a pointer to your structure. The
message is not copied, you simply allow the other program to
use some part of your own code.
It is important to remember that after you have sent a message
the other program may read and alter it. You are therefore not
allowed to use the message structure until the other program
has replied. After the other program has replied you may of
course do whatever you want with the structure. Note that the
other program may have changed the message.
Before your program terminates you must have received a reply
for every message you have sent! If there exist a message you
have not received any reply for and your program terminates,
the message structure is deallocated, and if the other program
tries now to change the message..., a disaster may occur!
4.2.1 MESSAGES
The message you want to send consist of two parts. The first
part is the Message structure which is used by Exec to monitor
all messages. The other part is the message itself. The whole
message should be allocated as PUBLIC memory since several (at
least two) tasks is using it. It must also be initialized
before it is dispatched.
As said above, the first part of the message must contain a
Message structure, which look like this: (defined in header
file "exec/ports.h")
struct Message
{
struct Node mn_Node;
struct *MsgPort *mn_ReplyPort;
UWORD mn_Length;
};
mn_Node: A Node structure used to link the message to
the message port. (See previous chapter for more
information about lists.) The "ln_Type" field of
the Node structure should be set to "NT_MESSAGE".
mn_ReplyPort: Pointer to a message port to which the other
task's reply will be sent to. (After you have
sent a message you must receive a reply at this
port before you may deallocate the message's
memory.)
mn_Length: The total length of the whole message.
The other part is the message itself that you want to send. It
may be anything from 0 up to almost 64KB long. So if you want
to send a message containing some information about a person,
the complete message structure should look something like this:
struct MyMessage
{
struct Message System_msg; /* Used be Exec. */
float height; /* The message itself... */
float weight;
int age;
char name[ 20 ];
};
The memory containing the message must be made "public", which
means that more than one task may read and alter it. The
easiest way to make it public is to allocate the memory with
help of the function AllocMem(). Set the parameter MEMF_PUBLIC
to make it public and MEMF_CLEAR to set all memory to 0.
/* Declare a pointer to your message: */
struct MyMessage *msg;
/* Allocate enough memory for the message: */
/* ("Clear the memory and make it public") */
msg = (struct MyMessage *)
AllocMem( sizeof( struct MyMessage ), MEMF_PUBLIC | CLEAR );
4.2.2 PREPARE MESSAGES
Before you may send the message you have to initialize some
parts of the message:
1. You must set the "ln_Type" field of the Node structure
to NT_MESSAGE. For example:
/* It is a message: */
msg->System_msg.mn_Node.ln_Type = NT_MESSAGE;
2. You need to specify which port the other program should
send the reply to when it has read the message. (It is
first when you receive a reply you may deallocate the
message's memory.) For example: ("replyport" is a pointer
to a MsgPort structure.)
/* Send replies to this port: */
msg->System_msg.mn_ReplyPort = replyport;
3. You also have to tell the system how long your message
is. You do it by setting the "mn_Length" field to
sizeof( the whole message ). For example:
/* Set size: */
msg->System_msg.mn_Length = sizeof( struct MyMessage );
4. Finally you may set your own message, before you send it.
(If you are only interested in what the other program
will reply, you do not need to set any personal message.)
For example:
msg->height = 190.5;
msg->weight = 75.0;
msg->age = 22;
strcpy( msg->name, "Anders Bjerin" );
4.2.3 MESSAGE PORTS
Messages are sent to message ports where they are collected
by the other task. A message port is actually a MsgPort
structure that has been initialized in a special manner. The
MsgPort structure look like this: (define in the header file
"exec/ports.h")
struct MsgPort
{
struct Node mp_Node;
UBYTE mp_Flags;
UBYTE mp_SigBit;
struct Task *mp_SigTask;
struct List mp_MsgList;
};
mp_Node: Used by the system. This Node structure contains
among many things the name of this message port.
(Each port should have a name if you want other
tasks to send messages to it.) The priority and
type (NT_MSGPORT) is also stored in this field.
mp_Flags: This filed contains flags that tells the system
what it should do when a new message arrives. To
check which flag is set you first have to "mask"
the number with help of PF_ACTION before you
examine the "mp_Flags" field. Following flags
exist for the moment:
PA_SIGNAL: Each time a message arrives a signal
will occur. (Even if there are
several messages queued before this
one, a signal will be broadcasted.
See below for more information about
signals.)
PA_SOFTINT: Each time a message arrives a
software interrupt will occur.
PA_IGNORE: When a message will arrive nothing
will happen.
To check which flag is set, do like this: ("mp" is
a pointer to an initialized message port.)
if( mp->mp_Flags & PF_ACTION == PA_SIGNAL )
printf( "The PA_SIGNAL flag is set!\n" );
and so on...
mp_SigBit: If this port has the PA_SIGNAL flag set, this bit
number will be "signaled".
mp_SigTask: Pointer to the task that should be signaled when
a message arrives. (Most of the time it is the same
task that created the port that should be signaled.)
mp_MsgList: Pointer to the list header in which all messages
will be stored.
4.2.4 PREPARE MESSAGE PORTS
Luckily we do not have to initialize the MsgPort structure
ourself if we do not want to. We only have to call the function
CreatePort() and it will do everything for us.
CreatePort() allocates and initializes a MsgPort structure. If
you give it a string as first parameter it will also make the
port public. (A port that has a name can be found by other tasks
and is therefore "public".) If CreatePort() of some reason could
not create a message port it returns NULL, otherwise if
everything is OK it returns a pointer to the new MsgPort
structure.
Synopsis: msgp = CreatePort( name, pri );
msgp: (struct MsgPort *) Pointer to the new MsgPort
structure, or NULL if something went wrong.
name: (char *) Pointer to a string containing the name
of the message port, or NULL. If it is a string
the port will be made public (so other tasks can
find it) else only our task can use it.
msgp: (struct MsgPort *) Pointer to the MsgPort structure
that should be allocated.
pri: (BYTE) This message port's priority. Usually set
to 0 - normal priority.
When your program terminates you must close the message port
by calling the DeletePort() function!
Synopsis: DeletePort( msgp );
msgp: (struct MsgPort *) Pointer to the MsgPort structure,
that should be deallocated.
Here is an example of how to create and delete a message port:
/* Declare a pointer to our message port: */
struct MsgPort *mp;
/* Create the message port: */
/* (Make the port public, priority 0.) */
mp = CreatePort( "ACM postbox", 0 );
/* Check if we have received a message port or not: */
if( !mp )
/* ERROR! Could not create message port! */
...
/* Close message port: */
DeletePort( mp );
4.2.5 FIND OTHER TASK'S MESSAGE PORTS
If a program want to communicate with another program, it has
to find the other task's message port. To do this it must know
the name of that port and can then call the function FindPort()
which will return a pointer to it.
Synopsis: msgp = FindPort( name );
msgp: (struct MsgPort *) FindPort() will return a pointer
to the port we asked for, or NULL if it could not
find it.
name: (char *) Pointer to a string containing the name of
the port we are looking for.
If we want to send a message to the "ACM postbox" port which
we created above, do like this:
/* Declare a pointer to the port we want to find: */
struct MsgPort *acm_msgport;
/* Try to find port "ACM postbox": */
acm_msgp = FindPort( "ACM postbox" );
/* Have we found the port or not? */
if( !acm_msgp )
printf( "Could not find the message port!\n" );
4.2.6 SEND MESSAGES
Once you have found some other programs message port and you
have initialized a message you can send it by calling the
function PutMsg(). It will put your message at the end of
his queue of messages, and if the PA_SIGNAL flag was set
in his MsgPort structure, he will also receive a signal.
Synopsis: PutMsg( msgp, message );
msgp: (struct MsgPort *) Pointer to the message port
to which you want to send a message.
message: (struct Message *) Pointer to the whole message
area.
For example: (We send our message declared above to our message
port "ACM postbox" which we have just found.)
/* Send our message: */
PutMsg( acm_msgp, msg );
4.2.7 WAITING FOR MESSAGES TO ARRIVE
When you are waiting for a message to arrive and you do not
want to do anything else, you should put your task to sleep.
A sleeping task does not use any computer-time, which means that
other tasks which are maybe running will be executed faster.
There exist two ways of waiting for a message to arrive at a
port. The simplest way is to call the function WaitPort() which
will put the task to sleep until a message arrives.
Synopsis: msg = WaitPort( msgp );
msg: (struct Message *) When a message arrives the task
is woken up and WaitPort() returns a pointer to the
first message that has arrived. (There may arrive
several messages at the same time.)
msgp: (struct MsgPort *) Pointer to the message port you
want to wait at.
The disadvantage with the WaitPort() function is that you can
only wait for messages to be received at one port. If you want
to monitor several ports you have to use the function Wait().
If the message port's PA_SIGNAL flag was set we will receive a
"signal" each time a message is put into our queue of received
messages. But what is a signal actually? It is about time to
explain it. However, if you are already confused you do not
have to understand this. Simply do as in the examples, and it
will work perfect.
Each time something happens, like a message arrives, the task
also receives a signal. Each task may monitor up to 32
different signals. The good thing about these signals is
that you can tell the operating system (Exec) that you want to
wait until something happens, and while you are waiting the
task is put to sleep.
You specify what conditions should wake up the task, by telling
Exec which signals should be received before the task should
be forced to work again. If you want to wait until a message
is received at the message port "msgp", you should give Exec
that port's signal number. (Each port is assigned a signal
number when they are created.) The field "msgp->mp_SigBit"
contains the message port's signal number.
Use the function Wait() to put a task to sleep if you want to
monitor several message ports.
Synopsis: rsig = Wait( wsig )
rsig: (ULONG) When one of the specified signal bits was
received, the task is woken up and Wait() returns
the signal bit value that woke it up.
wsig: (ULONG) Wait() will put the task to sleep, and it
will only wake up if one of the specified signal
bit values is received.
Here is an example that may make it a bit more understandable:
(We want to monitor three message ports which we have already
found, and they are: mp_meny, mp_gadget, mp_timer.)
ULONG meny_sig, gadget_sig, timer_sig, received_sig;
/* Get the ports signal values and transform it into bit */
/* fields. Example: */
/* Signal value -> Bit field */
/* ------------------------------------------------ */
/* 16 -> 00000000000000010000000000000000 */
/* 17 -> 00000000000000100000000000000000 */
/* 18 -> 00000000000001000000000000000000 */
meny_sig = 1 << mp_meny->mp_SigBit;
gadget_sig = 1 << mp_gadget->mp_SigBit;
timer_sig = 1 << mp_timer->mp_SigBit;
/* Wait until we receive a message at one of the ports: Zzz */
received_sig = Wait( meny_sig | gadget_sig | timer_sig );
/* Which port sent us the signal? */
if( received_sig & meny_sig )
printf( "The Menu port!" );
if( received_sig & gadget_sig )
printf( "The Gadget port!" );
if( received_sig & timer_sig )
printf( "The Timer port!" );
To put a task to sleep until a message arrives at the message
port "msgp", write:
4.2.8 COLLECT MESSAGES
To collect a message use the function GetMsg(). It will remove
the message from that port's message list, and return a pointer
to the message. If it could not find any message it returns
NULL. Before your program terminates it should try to collect
all waiting messages.
Synopsis: msg = GetMsg( msgp );
msg: (struct Message *) If GetMsg() could find a
message at the port it returns a pointer to the
message, and removes the message from the queue.
If no message was found, it returns NULL.
msgp: (struct MsgPort *) Pointer to the message port you
want to get the message from.
For example: (It will try to collect a message, type MyMessage,
from the port "msgp".)
/* Declare a pointer to the message we hopefully */
/* will be able to collect: (MyMessage structure */
/* is defined above.) */
struct MyMessage *temp_msg;
/* Try to collect a message: */
temp_msg = GetMsg( msgp );
/* Collect some values: */
printf( "%s is %d years old!", temp_msg->namn, temp_msg->age );
4.2.9 REPLY
When you have received a message, and examined and altered it
as desired it is time to reply. When you reply will the other
task be informed that you have finished using the message, and
the other task may now do what it want with it. After you have
replied you may not read or alter the message any more!
Use the function ReplyMsg() to reply to the sender. (ReplyMsg()
will send the message to the reply port found in the messages
"mn_ReplyPort field").
Synopsis: ReplyMsg( msg );
msg: (struct Message *) Pointer to the message you have
previously collected, and do not want to use any
more.
For example: (After we have used the message we received above,
we reply as soon as possible.)
ReplyMsg( temp_msg );
4.3 EXAMPLE
Here is a complete example of two programs that will try to
communicate with each other.
4.3.1 PROGRAM A
/* This program will create a message port called "NrPort". */
/* It will then go to sleep and will first wake up when it */
/* has received a message. It will collect the message, read */
/* and alter it before it replies and the program terminates. */
#include <exec/types.h> /* STRPTR */
#include <exec/ports.h> /* struct Message */
#include <exec/memory.h> /* MEMF_PUBLIC */
#include <exec/nodes.h> /* NT_MESSAGE */
/* Declare a pointer to our message port: */
struct MsgPort *msgp;
/* The message will come in this form: */
struct NrMessage
{
struct Message SystemMsg;
int number;
};
/* Declare a pointer to the message: */
struct NrMessage *nrmsg;
/* Declare the functions: */
void clean_up();
void main();
void main()
{
/* Create a message port: (Name "NrPort", priority 0) */
msgp = (struct MsgPort *) CreatePort( "NrPort", 0 );
/* Check if we have successfully created a port: */
if( !msgp )
clean_up( "Could not create the message port!" );
/* Wait for a message to arrive: */
printf( "A: Waiting for a message to arrive...\n" );
WaitPort( msgp );
/* Now one or more messages have arrived, try to collect */
/* as many messages as possible: */
while( nrmsg = (struct NrMessage *) GetMsg( msgp ))
{
/* Read the message: */
printf( "A: The value we received was: %d\n", nrmsg->number );
printf( "A: Lets change it!\n" );
/* Alter it: */
nrmsg->number = 67890;
/* Reply: */
ReplyMsg( nrmsg );
/* Note that after we have replied, we may not */
/* read or alter the message any more! */
}
clean_up( "The End!" );
}
void clean_up( text )
STRPTR text;
{
/* If we have successfully created a port, close it: */
if( msgp )
DeletePort( msgp);
/* Print any message: */
printf( "A: %s\n", text );
/* Quit: */
exit( 0 );
}
4.3.2 PROGRAM B
/* This program will create a message of type NrMessage. */
/* It will then try to find a message port called "NrPort". */
/* If it finds that port it will send a message to it, and */
/* wait for the other task to reply. */
#include <exec/types.h> /* STRPTR */
#include <exec/ports.h> /* struct Message */
#include <exec/memory.h> /* MEMF_PUBLIC */
#include <exec/nodes.h> /* NT_MESSAGE */
/* Declare a pointer to our message and reply port: */
struct MsgPort *msgp, *replymsgp;
/* The message will be in this form: */
struct NrMessage
{
struct Message SystemMsg;
int number;
};
/* Declare a pointer to the message: */
struct NrMessage *nrmsg;
/* Declare the functions: */
void clean_up();
void main();
void main()
{
/* Allocate memory for the message: */
/* (Make it public and clear it.) */
nrmsg = (struct NrMessage *)
AllocMem( sizeof( struct NrMessage ), MEMF_PUBLIC|MEMF_CLEAR );
/* Check if we have allocated the memory successfully: */
if( !nrmsg )
clean_up( "Not enough memory for message!" );
/* Try to find the message port "NrPort": */
msgp = (struct MsgPort *) FindPort( "NrPort" );
/* Have we found the message port? */
if( !msgp )
clean_up( "Could not find the message port!" );
/* Create a reply port: (When the other task replies */
/* we will receive a message at this port.) */
replymsgp = (struct MsgPort *) CreatePort( "NrReplyPort", 0 );
/* Have we received a reply port? */
if( !replymsgp )
clean_up( "Could not create the reply port!" );
/* Initialize the message: */
/* Set type to NT_MESSAGE: */
nrmsg->SystemMsg.mn_Node.ln_Type = NT_MESSAGE;
/* Give the message a pointer to our reply port: */
nrmsg->SystemMsg.mn_ReplyPort = replymsgp;
/* Set the length of our message: */
nrmsg->SystemMsg.mn_Length = sizeof( struct NrMessage );
/* Set the message itself: */
nrmsg->number = 12345;
/* Inform the user: */
printf( "B: We have now successfully prepared everything!\n" );
printf( "B: Lets send the value: %d\n", nrmsg->number );
/* Send the message: */
PutMsg( msgp, nrmsg );
/* After we have sent our message and until we */
/* receive a reply we are not allowed to read */
/* or alter the message any more! */
printf( "B: Message sent, lets wait...\n" );
/* Wait at our reply port: */
WaitPort( replymsgp );
/* Try to receive as many messages as possible */
/* from the reply port. (In this example we will */
/* only receive one reply, but you never know.) */
while( GetMsg( replymsgp ))
{
/* We may now examine the message: */
printf( "B: We have received a reply!\n" );
printf( "B: The value is now: %d\n", nrmsg->number );
}
/* The End! */
clean_up( "The End!" );
}
void clean_up( text )
STRPTR text;
{
/* If we have successfully allocated memory for a message */
/* deallocate it. (Note that we may not deallocate this */
/* message if we have sent it to another task's message */
/* port, and have not received any reply!) */
if( nrmsg )
FreeMem( nrmsg, sizeof( *nrmsg ) );
/* If we have we successfully created a reply */
/* port, close it: */
if( replymsgp )
DeletePort( replymsgp);
/* Print any message: */
printf( "B: %s\n", text );
/* Quit: */
exit( 0 );
}
4.4 FUNCTIONS
CreatePort()
CreatePort() allocates and initializes a MsgPort structure.
If you give it a string as first parameter it will also make
the port public. (A port that has a name can be found by
other tasks and is therefore "public".) If CreatePort() of
some reason could not create a message port it returns NULL,
otherwise if everything is OK it returns a pointer to the
new MsgPort structure.
Synopsis: msgp = CreatePort( name, pri );
msgp: (struct MsgPort *) Pointer to the new MsgPort
structure, or NULL if something went wrong.
name: (char *) Pointer to a string containing the name
of the message port, or NULL. If it is a string
the port will be made public (so other tasks can
find it) else only our task can use it.
msgp: (struct MsgPort *) Pointer to the MsgPort structure
that should be allocated.
pri: (BYTE) This message port's priority. Usually set
to 0 - normal priority.
DeletePort()
DeletePort() will close a message port that is presently
open. All ports that has been created must be closed before
the program may terminate.
Synopsis: DeletePort( msgp );
msgp: (struct MsgPort *) Pointer to the MsgPort structure,
that should be closed.
FindPort()
FindPort() will try to locate an already existing message
port. If the function finds the port it returns a pointer to
it, else the function returns NULL.
Synopsis: msgp = FindPort( name );
msgp: (struct MsgPort *) FindPort() will return a pointer
to the port we asked for, or NULL if it could not
find it.
name: (char *) Pointer to a string containing the name of
the port we are looking for.
PutMsg()
PutMsg() will put a message at the end of the port's queue
of messages. Note that after you have sent a message you may
not read or alter it until the other task has replied!
Synopsis: PutMsg( msgp, message );
msgp: (struct MsgPort *) Pointer to the message port
to which you want to send a message.
message: (struct Message *) Pointer to the whole message
area.
WaitPort()
WaitPort() will put the task to sleep, and will first wake
up when a message arrives. While the task is sleeping it does
not use any computer time.
Synopsis: msg = WaitPort( msgp );
msg: (struct Message *) When a message arrives the task
is woken up and WaitPort() returns a pointer to the
first message that has arrived. (There may arrive
several messages at the same time.)
msgp: (struct MsgPort *) Pointer to the message port you
want to wait at.
Wait()
Wait() will put the task to sleep, and will first wake up
when one of the specified signal bits arrives. The advantage
with this function compared to WaitPort() is that you can
monitor several message ports at the same time. You only
need to tell Wait() which signal you want to wait for, and
once one or more of them arrives the task is put to work
again.
Synopsis: rsig = Wait( wsig )
rsig: (ULONG) When one of the specified signal bits was
received, the task is woken up and Wait() returns
the signal bit value that woke it up.
wsig: (ULONG) Wait() will put the task to sleep, and it
will only wake up if one of the specified signal
bit values is received.
GetMsg()
GetMsg() will try to remove a message from the port's
message list, and return a pointer to the message. If it
could not find any message it returns NULL. Before your
program terminates it should try to collect all waiting
messages.
Synopsis: msg = GetMsg( msgp );
msg: (struct Message *) If GetMsg() could find a
message at the port it returns a pointer to the
message, and removes the message from the queue.
If no message was found, it returns NULL.
msgp: (struct MsgPort *) Pointer to the message port you
want to get the message from.
ReplyMsg()
ReplyMsg() replies to the sender. All messages you have
collected should be replied. Note that after you have replied
you may not use the message any more!
Synopsis: ReplyMsg( msg );
msg: (struct Message *) Pointer to the message you have
previously collected, and do not want to use any
more.
4.5 EXAMPLES
Example 1
A: This program will create a message port called "NrPort".
It will then go to sleep and will first wake up when it has
received a message. It will collect the message, read and
alter it before it replies and the program terminates.
B: This program will create a message of type NrMessage. It
will then try to find a message port called "NrPort". If it
finds that port it will send a message to it, and wait for
the other task to reply.
Start program A then program B.
Example 2
This program will open the Timer Device to which we connect
a message port. When the Timer Device has done our request
(waiting for 10 seconds) it will send a message to the
message port which tells us that the time has passed.
This example demonstrates how you can give the system (like
the Timer Device) a message port through which the system can
communicate with us. Although we are using the Timer Device,
it is only as a demonstration on how to work with message
ports. For more information about the Timer Device, see
chapter Timer Device.
Example 3
This program will open two ports. The first port is used by
the Timer Device while the second port will not be used at
all (it is a dummy port). The reason why this example opens
an extra port is because I want to demonstrate how you can
handle two message ports simultaniously. With this technique
you can monitor up to 32 ports (the system usually needs some
singnals, but you have at least 16 signals for yourself).