A player thread may sleep on input or output, except:
(1) While it is executing a C_MOD command, it may only sleep on input.
(2) While it is being aborted by the update or shutdown, it may not
sleep at all.
To find out whether a player thread may sleep on input, code has to
check condition (2). It needs do to that in recvclient().
To find out whether it may sleep on output, it has to check both
conditions. It needs to do that in pr_player() and upr_player().
The code tracked condition (1) in global variable play_lock_wanted.
It checked condition (2) by examining struct player member command.
Replace all that by new struct player member may_sleep. Initialize it
in player_new(), update it in dispatch(), shutdwn() and update_run().
This makes the tests in recvclient(), pr_player() and upr_player()
obvious. play_wrlock_wanted() is now unused, remove it.
#define CAP bit(6)
#define MONEY bit(7)
#define CAP bit(6)
#define MONEY bit(7)
+enum player_sleep {
+ PLAYER_SLEEP_NEVER, PLAYER_SLEEP_ON_INPUT, PLAYER_SLEEP_FREELY
+};
+
struct player {
struct emp_qelem queue;
empth_t *proc;
struct player {
struct emp_qelem queue;
empth_t *proc;
int simulation; /* e.g. budget command */
double dolcost;
time_t curup; /* when last input was received */
int simulation; /* e.g. budget command */
double dolcost;
time_t curup; /* when last input was received */
+ enum player_sleep may_sleep; /* when may thread sleep? */
int aborted; /* interrupt cookie or EOF received? */
int eof; /* EOF (cookie or real) received? */
int recvfail; /* #recvclient() failures */
int aborted; /* interrupt cookie or EOF received? */
int eof; /* EOF (cookie or real) received? */
int recvfail; /* #recvclient() failures */
extern int shutdown_pending;
extern empth_rwlock_t *play_lock;
extern int shutdown_pending;
extern empth_rwlock_t *play_lock;
-extern int play_wrlock_wanted;
extern int update_running;
extern time_t update_time[UPDATE_TIME_LEN];
extern int update_running;
extern time_t update_time[UPDATE_TIME_LEN];
}
emp_insque(&lp->queue, &Players);
lp->cnum = NATID_BAD;
}
emp_insque(&lp->queue, &Players);
lp->cnum = NATID_BAD;
+ lp->may_sleep = PLAYER_SLEEP_FREELY;
lp->curid = -1;
time(&lp->curup);
}
lp->curid = -1;
time(&lp->curup);
}
pr("Command not implemented\n");
return 0;
}
pr("Command not implemented\n");
return 0;
}
+ player->may_sleep = command->c_flags & C_MOD
+ ? PLAYER_SLEEP_ON_INPUT : PLAYER_SLEEP_FREELY;
player->command = command;
empth_rwlock_rdlock(play_lock);
if (redir) {
player->command = command;
empth_rwlock_rdlock(play_lock);
if (redir) {
}
empth_rwlock_unlock(play_lock);
player->command = NULL;
}
empth_rwlock_unlock(play_lock);
player->command = NULL;
+ player->may_sleep = PLAYER_SLEEP_FREELY;
#include "journal.h"
#include "player.h"
#include "prototypes.h"
#include "journal.h"
#include "player.h"
#include "prototypes.h"
/*
* Receive a line of input from the current player.
/*
* Receive a line of input from the current player.
* Flush all queued output before potentially sleeping in
* io_input(), to make sure player sees the prompt.
*/
* Flush all queued output before potentially sleeping in
* io_input(), to make sure player sees the prompt.
*/
- while (io_output(player->iop, !play_wrlock_wanted) > 0) ;
+ while (io_output(player->iop,
+ player->may_sleep >= PLAYER_SLEEP_ON_INPUT) > 0)
+ ;
/*
* If io_output_all() blocked and got unblocked by command
/*
* If io_output_all() blocked and got unblocked by command
if (player == pl) {
while (io_output_if_queue_long(pl->iop,
if (player == pl) {
while (io_output_if_queue_long(pl->iop,
- !play_wrlock_wanted
- && !(pl->command && (pl->command->c_flags & C_MOD)))
- > 0)
+ pl->may_sleep == PLAYER_SLEEP_FREELY) > 0)
if (player == pl) {
while (io_output_if_queue_long(pl->iop,
if (player == pl) {
while (io_output_if_queue_long(pl->iop,
- !play_wrlock_wanted
- && !(pl->command && (pl->command->c_flags & C_MOD)))
- > 0)
+ pl->may_sleep == PLAYER_SLEEP_FREELY) > 0)
*/
empth_rwlock_t *play_lock;
*/
empth_rwlock_t *play_lock;
-/*
- * Is a thread attempting to take an exclusive play_lock?
- * Threads holding a shared play_lock must not sleep while this is
- * true.
- */
-int play_wrlock_wanted;
-
static char pidfname[] = "server.pid";
/* Run as daemon? If yes, detach from controlling terminal etc. */
static char pidfname[] = "server.pid";
/* Run as daemon? If yes, detach from controlling terminal etc. */
logerror("Shutdown commencing (cleaning up threads.)");
logerror("Shutdown commencing (cleaning up threads.)");
- play_wrlock_wanted = 1;
for (p = player_next(NULL); p; p = player_next(p)) {
if (p->state != PS_PLAYING)
continue;
pr_flash(p, "Server shutting down...\n");
p->state = PS_SHUTDOWN;
for (p = player_next(NULL); p; p = player_next(p)) {
if (p->state != PS_PLAYING)
continue;
pr_flash(p, "Server shutting down...\n");
p->state = PS_SHUTDOWN;
+ p->may_sleep = PLAYER_SLEEP_NEVER;
p->aborted++;
if (p->command) {
pr_flash(p, "Shutdown aborting command\n");
p->aborted++;
if (p->command) {
pr_flash(p, "Shutdown aborting command\n");
- play_wrlock_wanted = 1;
for (p = player_next(NULL); p; p = player_next(p)) {
if (p->state != PS_PLAYING)
continue;
if (p->command) {
pr_flash(p, "Update aborting command\n");
for (p = player_next(NULL); p; p = player_next(p)) {
if (p->state != PS_PLAYING)
continue;
if (p->command) {
pr_flash(p, "Update aborting command\n");
+ p->may_sleep = PLAYER_SLEEP_NEVER;
p->aborted = 1;
empth_wakeup(p->proc);
}
p->aborted = 1;
empth_wakeup(p->proc);
}
empth_rwlock_wrlock(play_lock);
if (*pre_update_hook) {
if (run_hook(pre_update_hook, "pre-update")) {
empth_rwlock_wrlock(play_lock);
if (*pre_update_hook) {
if (run_hook(pre_update_hook, "pre-update")) {
- play_wrlock_wanted = 0;
empth_rwlock_unlock(play_lock);
return;
}
}
update_running = 1;
update_main();
empth_rwlock_unlock(play_lock);
return;
}
}
update_running = 1;
update_main();
- play_wrlock_wanted = update_running = 0;
empth_rwlock_unlock(play_lock);
}
empth_rwlock_unlock(play_lock);
}