bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Some questions about libports and notification of ports


From: Thomas Bushnell BSG
Subject: Re: Some questions about libports and notification of ports
Date: Fri, 22 Aug 2008 15:14:36 -0700

On Fri, 2008-08-22 at 02:54 +0200, zhengda wrote:
> When a port_info object is created by ports_create_port() and 
> ports_get_right() hasn't been called,
> we have to call ports_port_deref() to destroy the port_info object.

You are still organizing things wrongly, and that's what's messing you
up.  You should basically *never* be thinking "how will I destroy the
port_info object?"  That you are thinking this is already a symptom of
the difficulty.  It is your *setup* that is wrong.

struct foo {
  struct port_info *a, *b;
  other_stuff;
}; 

is WRONG.  It will remain wrong, no matter how many times you want it to
work.  What you want is:

struct foo {
  other_stuff;
};

struct foo_a {
  struct port_info pi;
  struct foo *foo;
  ...
};

struct foo_b {
  struct port_info pi;
  struct foo *foo;
  ...
};

I've now said this twice.

ports_port_deref is not "the way to destroy a port_info object"; rather, it 
deletes a reference to it.
When all references go away, of course, the object is automatically destroyed.

ports_port_destroy is not "the way to destroy a port_info object";
rather, it is the way to destroy a receive right in the very unusual
case that some external sort of thing has required its destruction.
It's hard for me to even think of a case where it's the right thing, it
is so rare.

There is no function which is "the way to destroy a port_info object".
This is intentional, and if you use the interface properly, you won't
need one.

> struct port_info *foo;
> ports_create_port(portclass, bucket, sizeof(*foo), &foo);
> ....
> ports_port_deref(foo);      // ports_port_destroy(foo) doesn't work here.

It works just fine, *if your intention is to delete the receive right*--
a rare and unusual thing.

> When a port_info object is created and ports_get_right() is called,
> struct port_info *foo;
> mach_port_t port;
> ports_create_port(portclass, bucket, sizeof(*foo), &foo);
> port=ports_get_right(foo);
> In this case, there are two ways to destroy the port_info object:
> either to call ports_port_deref() and then ports_destroy_right() explicitly,
> or to call ports_port_deref() and then wait until no_senders 
> notification is generated.

NO, NO, NO.  The reference count is poor-man's-gc.  It's garbage
collection.  You are responsible for maintaining the reference count,
and letting the object be destroyed *on it's own*.  

EVERY live pointer to the object must be accounted as a reference.  Note
how badly your scheme works:

struct foo {
  port_info *a, *b;
};

If you write that, then every struct foo *MUST* hold a reference to the
A and the B.  And, as a result, they will never get gc'd, because you
will never see the references go to zero.

> One end of the pipe is the user program and the other is the device, so 
> I need to create two port_info objects for the pipe.

Yes, this is correct.

> When the user program exits, the port for it can be destroyed by 
> no-sender notification,

You are thinking "how can we destroy the port", which is the wrong
question.  But yes, when the user has had this event, the port can be
destroyed.

> and meanwhile, the other port should also be destroyed, 

WHY?  What possible value is there in destroying the other port?

Notice, for example, the way pipes work.  Suppose the reader dies, and
there are no readers.  We do not destroy the writer's port.  Heaven's
no!  Instead, we accept requests on it, just as normal, *and we return
EPIPE*.

We don't need to destroy the port, nor should we.  It still has users.
Yes, their requests may fail, but that doesn't mean there aren't still
people holding send rights who are entitled to see the protocol
continue.

> If the port for the device isn't destroy with ports_destroy_right(), it 
> might never be destroyed
> because its send right might always be kept by the device, and 
> no-senders notification will never be generated.

I'm confused.  How does a device hold send rights to your port?  Mach devices 
don't work that way.  What is this "device" that is holding a send right?

> 
> Another thing surprises me is that in some code of hurd, 
> ports_port_deref() is used with ports_get_right() together. For example,
> error_t
> S_io_duplicate (struct sock_user *user,
>         mach_port_t *newobject,
>         mach_msg_type_name_t *newobject_type)
> {
>   struct sock_user *newuser;
>   if (!user)
>     return EOPNOTSUPP;
> 
>   __mutex_lock (&global_lock);
>   newuser = make_sock_user (user->sock, user->isroot, 0, 0);
>   *newobject = ports_get_right (newuser);
>   *newobject_type = MACH_MSG_TYPE_MAKE_SEND;
>   ports_port_deref (newuser);
>   __mutex_unlock (&global_lock);
>   return 0;
> }

When we create the port_info, it has one reference, held by the variable
NEWUSER.  Then we call ports_get_right and create a send right.  Now
there are two references, one held by NEWUSER, and one held by the
outstanding send rights.  Then we release the references held by
NEWUSER, and return.

At the end of the function, the reference count is 1, and there is one
reference outstanding (the send rights).

You are busy thinking "how can we get things destroyed", in a way that
suggests you don't understand what reference counting is for.

> ports_port_deref() is need to be called here, otherwise the port_info 
> object cannot be destroyed by ports_port_destroy() or no-senders 
> notification later.
> The code is right here because ports_get_right() may be called on the 
> port only once.

Huh?  You may call ports_get_right on a port as many times as you can.  

> But sometimes we do need to call ports_get_right() on a port several times.
> When should we call ports_port_deref() to decrease the reference count 
> that is created by the first ports_get_right()?

In the code above, we aren't dropping the reference from
ports_get_right, that would be crazy.  We are *creating* that reference.
We are dropping the reference that was created by make_sock_user.

All outstanding send rights on a port share a single reference.  This
matches exactly the behavior of Mach's no-senders notification.  So if
there are already send rights, ports_get_right doesn't create a
reference because the new send right shares the same reference as the
old.

> I thought, if notify is null, the no-senders notification can be 
> generated, and it can be receive by the process someway.

No, the port is the place where things are sent.  By clearing that port,
you tell the kernel not to send the notification any more.

Thomas






reply via email to

[Prev in Thread] Current Thread [Next in Thread]