Path: utzoo!mnetor!uunet!lll-winken!lll-lcc!ames!pasteur!ucbvax!CORY.BERKELEY.EDU!dillon From: dillon@CORY.BERKELEY.EDU (Matt Dillon) Newsgroups: comp.sys.amiga Subject: Re: IPC - A proposal Message-ID: <8803100221.AA01669@cory.Berkeley.EDU> Date: 10 Mar 88 02:21:43 GMT Sender: daemon@ucbvax.BERKELEY.EDU Lines: 126 >How about this, though? A valid public port always has a name, therefore >it has a specific pointer in its name field. If ValidPort() checks on >this, there is essentially no way it can make an error (unless a subsequent >program planted a pointer to an identically-addressed data item in the same >place!). An aborting server does a RemPort, then zeroes the name pointer >to prevent further messages. > >We're making progress... > > -- Pete -- The Proper solution of the public-port randezvous problem is to extend the port structure (via allocating a bit more memory than the normal PORT structure size) to include a reference count field. The owner of the port (the one receiving messages) simply creates it with the reference count set to 1. When the owner goes away, it sets the mp_Task field in the port to NULL and decrements the reference count, deleting the port if rcount == 0: When creating the port, the owner would first check to see if it exists with FindPort() under Forbid(), and if so and if no other owner is attached, can simply reconnect: SPECIALPORT { MSGPORT port; long count; }; SPECIALPORT * OwnerOpen(name) char *name; { SPECIALPORT *port; Forbid(); if (port = FindPort(name)) { if (port->port.mp_Task) return(NULL); /* port already has a master */ port->port.mp_Task = FindTask(NULL); } else { port = CreateExtendedPort(name, sizeof(SPECIALPORT)); /* NOTE: CreateExtendedPort allocates a copy of the name * since there is no guarentee it will stay around */ } port->count = 1; Permit(); return(port); } SPECIALPORT * ApplOpen(name) char *name; { SPECIALPORT *port; Forbid(); if ((port = FindPort(name)) && port->port.mp_Task) { ++port->count; Permit(); return(port); } Permit(); return(NULL); } OwnerClose(port) SPECIALPORT *port; { Forbid(); while (ior = GetMsg(port)) { ior->io_Error = -1; ReplyMsg(ior); } port->port.mp_Task = NULL; if (--port->count == 0) DeleteExtendedPort(port, sizeof(SPECIALPORT)); Permit(); } ApplClose(port) SPECIALPORT *port; { Forbid(); if (--port->count == 0) DeleteExtendedPort(port, sizeof(SPECIALPORT)); Permit(); } Anybody intending to send messages to the port, does ApplOpen() ONCE, which incrementing the reference count while Forbid()n. Every time he PutMsg()'s to the port, he checks the mp_Task field, and if NULL AND the message has not been returned (in case the master shutsdown after our PutMsg() but before we check mp_Task), Forbid()s, does a second check while Forbid()n, pulls it off and returns an error: void ApplSendMsg(port,ior) SPECIALPORT *port; IOR *ior; { ior->io_Message.mn_Node.nt_Type = NT_MESSAGE; PutMsg(port, ior) if (port->port.mp_Task == NULL && ior->io_Message.mn_Node.nt_Type != NT_REPLYMSG); Forbid(); if (ior->io_Message.mn_Node.nt_Type != NT_REPLYMSG) { Remove(ior); ior->io_Error = IOERR_NOMASTER; ReplyMsg(ior); } Permit(); } } Note that this works in all timing cases (because nothing bad happens if you PutMsg to a port with a NULL mp_Task field. NOTE THAT THERE IS ESSENTIALLY NO OVERHEAD FOR NORMAL OPERATION. The check inside the Forbid() is needed in case the master (or another master) re-connects to the port. When the application is through with the port, it simply decrements the reference count while Forbid()n, and if 0, removes the port (essentially the same thing the master does when through with the port). -Matt