blob: 3c2b1722f91ff14ae8e96783c86af5e8b0febf52 [file] [log] [blame]
/*
* Copyright (c) 2000-2001 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <pthread.h>
#include <assert.h>
#include <string.h>
#include "config.h"
#include "types.h"
#include "exit.h"
#include "stream.h"
#include "lock.h"
#include "mlog.h"
#define N(a) (sizeof((a)) / sizeof((a)[0]))
struct spm {
stream_state_t s_state;
pthread_t s_tid;
int s_ix;
int s_exit_code;
rv_t s_exit_return;
rv_t s_exit_hint;
};
typedef struct spm spm_t;
static spm_t spm[STREAM_SIMMAX * 3];
static bool_t initialized = BOOL_FALSE;
void
stream_init(void)
{
(void)memset((void *)spm, 0, sizeof(spm));
initialized = BOOL_TRUE;
}
/*
* Note that the stream list structure (updated via the stream_* functions)
* is indexed by pthread_t (tid). Multiple processes can be registered against
* the same stream index, typically: the primary content process that does the
* work; and the drive worker process, which just processes stuff off the ring
* buffer. In general having multiple tids registered per stream is not an issue
* for termination status reporting, as the mlog_exit* logging functions only
* ever get called out of the primary content process.
*/
void
stream_register(pthread_t tid, int streamix)
{
spm_t *p = spm;
spm_t *ep = spm + N(spm);
assert(streamix < STREAM_SIMMAX);
lock();
for (; p < ep; p++) {
if (p->s_state == S_FREE) {
p->s_state = S_RUNNING;
break;
}
}
unlock();
assert(p < ep);
if (p >= ep) return;
p->s_tid = tid;
p->s_ix = streamix;
p->s_exit_code = -1;
p->s_exit_return = RV_NONE;
p->s_exit_hint = RV_NONE;
}
/* NOTE: lock() must be held when calling stream_dead() */
void
stream_dead(pthread_t tid)
{
spm_t *p = spm;
spm_t *ep = spm + N(spm);
for (; p < ep; p++)
if (pthread_equal(p->s_tid, tid)) {
p->s_state = S_ZOMBIE;
break;
}
assert(p < ep);
}
void
stream_free(pthread_t tid)
{
spm_t *p = spm;
spm_t *ep = spm + N(spm);
lock();
for (; p < ep; p++) {
if (pthread_equal(p->s_tid, tid)) {
(void) memset((void *) p, 0, sizeof(spm_t));
p->s_state = S_FREE;
break;
}
}
unlock();
assert(p < ep);
}
int
stream_find_all(stream_state_t states[], int nstates,
pthread_t tids[], int ntids)
{
int i, count = 0;
spm_t *p = spm;
spm_t *ep = spm + N(spm);
assert(nstates > 0 && ntids > 0);
if (!initialized)
return 0;
/* lock - make sure we get a consistent snapshot of the stream status */
lock();
for (; p < ep && count < ntids; p++)
for (i = 0; i < nstates; i++)
if (p->s_state == states[i]) {
tids[count++] = p->s_tid;
break;
}
unlock();
return count;
}
static spm_t *
stream_find(pthread_t tid, stream_state_t s[], int nstates)
{
int i;
spm_t *p = spm;
spm_t *ep = spm + N(spm);
assert(nstates > 0);
/* note we don't lock the stream array in this function */
for (; p < ep; p++)
if (pthread_equal(p->s_tid, tid)) {
/* check state */
for (i = 0; i < nstates; i++)
if (p->s_state == s[i])
return p;
}
#ifdef STREAM_DEBUG
{
static const char *state_strings[] = { "S_FREE", "S_RUNNING", "S_ZOMBIE" };
mlog(MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,
"stream_find(): no stream with tid: %lu and state%s:",
tid, nstates == 1 ? "" : "s");
for (i = 0; i < nstates; i++)
mlog(MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE,
" %s", state_strings[s[i]]);
mlog(MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK | MLOG_BARE, "\n");
}
#endif /* STREAM_DEBUG */
return (spm_t *) NULL;
}
/*
* Note, the following function is called from mlog.c:mlog_va(),
* where locking may be disabled and we are already protected by
* another lock. So no locking is done in this function.
*/
int
stream_getix(pthread_t tid)
{
stream_state_t states[] = { S_RUNNING };
spm_t *p;
int ix;
p = stream_find(tid, states, N(states));
ix = p ? p->s_ix : -1;
return ix;
}
/*
* We don't currently export spm_t and don't really want callers
* keeping pointers into the stream array, so use explicit access
* functions. Note we only expect these to be called from running/owner
* streams.
*/
#define stream_set(field_name, tid, value) \
stream_state_t states[] = { S_RUNNING }; \
spm_t *p; \
pthread_t mytid = pthread_self(); \
\
if (!pthread_equal(mytid, (tid))) { \
mlog(MLOG_DEBUG | MLOG_ERROR | MLOG_NOLOCK, \
"stream_set_" #field_name "(): " \
"foreign stream (tid %lu) " \
"not permitted to update this stream (tid %lu)\n",\
mytid, (tid)); \
return; \
} \
\
lock(); \
p = stream_find((tid), states, N(states)); \
if (p) p->s_exit_ ## field_name = (value); \
unlock();
void stream_set_code(pthread_t tid, int exit_code)
{
stream_set(code, tid, exit_code);
}
void stream_set_return(pthread_t tid, rv_t rv)
{
stream_set(return, tid, rv);
}
void stream_set_hint(pthread_t tid, rv_t rv)
{
stream_set(hint, tid, rv);
}
bool_t
stream_get_exit_status(pthread_t tid,
stream_state_t states[],
int nstates,
stream_state_t *state,
int *ix,
int *exit_code,
rv_t *exit_return,
rv_t *exit_hint)
{
bool_t sts = BOOL_FALSE;
spm_t *p;
lock();
p = stream_find(tid, states, nstates);
if (!p) goto unlock;
if (state) *state = p->s_state;
if (ix) *ix = p->s_ix;
if (exit_code) *exit_code = p->s_exit_code;
if (exit_return) *exit_return = p->s_exit_return;
if (exit_hint) *exit_hint = p->s_exit_hint;
sts = BOOL_TRUE;
unlock:
unlock();
return sts;
}
size_t
stream_cnt(void)
{
spm_t *p = spm;
spm_t *ep = spm + N(spm);
size_t ixmap = 0;
size_t ixcnt;
size_t bitix;
assert(sizeof(ixmap) * NBBY >= STREAM_SIMMAX);
lock();
for (; p < ep; p++) {
if (p->s_state == S_RUNNING) {
ixmap |= (size_t)1 << p->s_ix;
}
}
unlock();
ixcnt = 0;
for (bitix = 0; bitix < STREAM_SIMMAX; bitix++) {
if (ixmap & ((size_t)1 << bitix)) {
ixcnt++;
}
}
return ixcnt;
}