i've been having trouble with nsec.  i've been dealing with an application
that has ~128 processes.  it does not use the thread library, but these
processes all share memory.  unfortunately, this results in wild thrashing
of nsec(2) because there are more shared-memory processes than slots
in the fds[] table in /sys/src/libc/nsec.c.  a call to nsec can cause delays
of up to 1s.

unfortunately, this isn't the real motivation.  the real motivation is that
recently a few processes were added that need to have their own fd tables.
and unfortunately this causes nsec() to enter an infinite loop.

in looking at the code, there seemed to be a number of ways to fix one
particular problem, but the given algorithm seems resistant to a generally
correct solution.

it seemed to me that the _privates (see exec(2)) array was the way to go.
process-private memory will never by falsely shared between processes
that don't share fd tables.  i kept the global fd to keep from opening
nsec() too many times.

in the case of this application, instead of opening nsec() afresh on
nearly every call, it is now opened just 3 times.

one note is that while i'm aware of privalloc(2), i didn't use it.  the
implementation doesn't appear correct for shared-memory procs.
i think there are two issues
- locking is unnecessary.  the only preemptable unit of execution is
a process and each process is guarenteed to have its own instance
of _privates and _nprivates.
- for shared-memory procs, we will run out of privates because
the static privinit will be falsely shared.  privinit should be replaced
by using a private entry.

i've attached my proposed solution.

- erik

----

#include <u.h>
#include <libc.h>

extern  void    **_privates;
extern  int     _nprivates;

        int     fd      = -1;

/*
 * BUG: this is chosen by fiat and without coordination.
 * privalloc(2) does not appear safe in a shared-memory
 * environment.
 */
#define Fd      ((int*)_privates[0])

static uvlong order = 0x0001020304050607ULL;

static void
be2vlong(vlong *to, uchar *f)
{
        uchar *t, *o;
        int i;

        t = (uchar*)to;
        o = (uchar*)&order;
        for(i = 0; i < sizeof order; i++)
                t[o[i]] = f[i];
}

static int*
getfd(void)
{
        if(Fd != nil)
                return Fd;
        return &fd;
}

static void
reopen(int *fd)
{
        *fd = open("/dev/bintime", OREAD|OCEXEC);
}

vlong
nsec(void)
{
        int *p;
        uchar b[8];
        vlong t;

        p = getfd();
        if(*p == -1)
                reopen(p);
        if(pread(*p, b, sizeof b, 0) != sizeof b){
                if(p != Fd){
                        p = malloc(sizeof *p);
                        if(p == nil)
                                return 0;
                        _privates[0] = p;
                }
                reopen(p);
                if(pread(*p, b, sizeof b, 0) != sizeof b)
                        return 0;
        }
        be2vlong(&t, b);
        return t;
}

Reply via email to