run clang-format

This commit is contained in:
Bassem Girgis
2018-10-17 23:47:57 -05:00
parent 558e8e22da
commit c8a8949366
56 changed files with 4153 additions and 3900 deletions

104
.clang-format Normal file
View File

@@ -0,0 +1,104 @@
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: TopLevelDefinitions
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces : Custom
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeInheritanceComma: true
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeCategories:
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: false
IndentWidth: 2
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: false
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 2
UseTab: Never

View File

@@ -73,7 +73,13 @@ ch04/pipe_cpp.cpp
NAMES_CXX=$(SOURCES_CXX:.cpp=) NAMES_CXX=$(SOURCES_CXX:.cpp=)
PROGRAMS_CXX=$(addprefix $(BIN)/, $(NAMES_CXX)) PROGRAMS_CXX=$(addprefix $(BIN)/, $(NAMES_CXX))
all: dirs ${PROGRAMS_C} ${PROGRAMS_CXX} all: format dirs ${PROGRAMS_C} ${PROGRAMS_CXX}
format:
#clang-format -i $(SOURCE)/*.c*
#clang-format -i $(SOURCE)/*.h*
clang-format -i $(SOURCE)/*/*.c*
clang-format -i $(SOURCE)/*/*.h*
dirs: dirs:
mkdir -p $(BIN) mkdir -p $(BIN)

View File

@@ -7,28 +7,31 @@
*/ */
#include "errors.h" #include "errors.h"
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
int seconds; int seconds;
char line[128]; char line[128];
char message[64]; char message[64];
while (1) { while (1) {
printf ("Alarm> "); printf("Alarm> ");
if (fgets (line, sizeof (line), stdin) == NULL) exit (0); if (fgets(line, sizeof(line), stdin) == NULL)
if (strlen (line) <= 1) continue; exit(0);
if (strlen(line) <= 1)
continue;
/* /*
* Parse input line into seconds (%d) and a message * Parse input line into seconds (%d) and a message
* (%64[^\n]), consisting of up to 64 characters * (%64[^\n]), consisting of up to 64 characters
* separated from the seconds by whitespace. * separated from the seconds by whitespace.
*/ */
if (sscanf (line, "%d %64[^\n]", if (sscanf(line, "%d %64[^\n]", &seconds, message) < 2) {
&seconds, message) < 2) { fprintf(stderr, "Bad command\n");
fprintf (stderr, "Bad command\n");
} else {
sleep (seconds);
printf ("(%d) %s\n", seconds, message);
}
} }
else {
sleep(seconds);
printf("(%d) %s\n", seconds, message);
}
}
} }

View File

@@ -8,49 +8,53 @@
#include <wait.h> #include <wait.h>
#include "errors.h" #include "errors.h"
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
int status; int status;
char line[128]; char line[128];
int seconds; int seconds;
pid_t pid; pid_t pid;
char message[64]; char message[64];
while (1) { while (1) {
printf ("Alarm> "); printf("Alarm> ");
if (fgets (line, sizeof (line), stdin) == NULL) exit (0); if (fgets(line, sizeof(line), stdin) == NULL)
if (strlen (line) <= 1) continue; exit(0);
if (strlen(line) <= 1)
continue;
/* /*
* Parse input line into seconds (%d) and a message * Parse input line into seconds (%d) and a message
* (%64[^\n]), consisting of up to 64 characters * (%64[^\n]), consisting of up to 64 characters
* separated from the seconds by whitespace. * separated from the seconds by whitespace.
*/ */
if (sscanf (line, "%d %64[^\n]", if (sscanf(line, "%d %64[^\n]", &seconds, message) < 2) {
&seconds, message) < 2) { fprintf(stderr, "Bad command\n");
fprintf (stderr, "Bad command\n");
} else {
pid = fork ();
if (pid == (pid_t)-1)
errno_abort ("Fork");
if (pid == (pid_t)0) {
/*
* If we're in the child, wait and then print a message
*/
sleep (seconds);
printf ("(%d) %s\n", seconds, message);
exit (0);
} else {
/*
* In the parent, call waitpid() to collect any children that
* have already terminated.
*/
do {
pid = waitpid ((pid_t)-1, NULL, WNOHANG);
if (pid == (pid_t)-1)
errno_abort ("Wait for child");
} while (pid != (pid_t)0);
}
}
} }
else {
pid = fork();
if (pid == (pid_t) -1)
errno_abort("Fork");
if (pid == (pid_t) 0) {
/*
* If we're in the child, wait and then print a message
*/
sleep(seconds);
printf("(%d) %s\n", seconds, message);
exit(0);
}
else {
/*
* In the parent, call waitpid() to collect any children that
* have already terminated.
*/
do {
pid = waitpid((pid_t) -1, NULL, WNOHANG);
if (pid == (pid_t) -1)
errno_abort("Wait for child");
} while (pid != (pid_t) 0);
}
}
}
} }

View File

@@ -8,52 +8,55 @@
#include "errors.h" #include "errors.h"
typedef struct alarm_tag { typedef struct alarm_tag {
int seconds; int seconds;
char message[64]; char message[64];
} alarm_t; } alarm_t;
void *alarm_thread (void *arg) void*
alarm_thread(void* arg)
{ {
alarm_t *alarm = (alarm_t*)arg; alarm_t* alarm = (alarm_t*) arg;
int status; int status;
status = pthread_detach (pthread_self ()); status = pthread_detach(pthread_self());
if (status != 0) if (status != 0)
err_abort (status, "Detach thread"); err_abort(status, "Detach thread");
sleep (alarm->seconds); sleep(alarm->seconds);
printf ("(%d) %s\n", alarm->seconds, alarm->message); printf("(%d) %s\n", alarm->seconds, alarm->message);
free (alarm); free(alarm);
return NULL; return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
int status; int status;
char line[128]; char line[128];
alarm_t *alarm; alarm_t* alarm;
pthread_t thread; pthread_t thread;
while (1) { while (1) {
printf ("Alarm> "); printf("Alarm> ");
if (fgets (line, sizeof (line), stdin) == NULL) exit (0); if (fgets(line, sizeof(line), stdin) == NULL)
if (strlen (line) <= 1) continue; exit(0);
alarm = (alarm_t*)malloc (sizeof (alarm_t)); if (strlen(line) <= 1)
if (alarm == NULL) continue;
errno_abort ("Allocate alarm"); alarm = (alarm_t*) malloc(sizeof(alarm_t));
if (alarm == NULL)
errno_abort("Allocate alarm");
/* /*
* Parse input line into seconds (%d) and a message * Parse input line into seconds (%d) and a message
* (%64[^\n]), consisting of up to 64 characters * (%64[^\n]), consisting of up to 64 characters
* separated from the seconds by whitespace. * separated from the seconds by whitespace.
*/ */
if (sscanf (line, "%d %64[^\n]", if (sscanf(line, "%d %64[^\n]", &alarm->seconds, alarm->message) < 2) {
&alarm->seconds, alarm->message) < 2) { fprintf(stderr, "Bad command\n");
fprintf (stderr, "Bad command\n"); free(alarm);
free (alarm);
} else {
status = pthread_create (
&thread, NULL, alarm_thread, alarm);
if (status != 0)
err_abort (status, "Create alarm thread");
}
} }
else {
status = pthread_create(&thread, NULL, alarm_thread, alarm);
if (status != 0)
err_abort(status, "Create alarm thread");
}
}
} }

View File

@@ -4,26 +4,27 @@
* Demonstrate detection of errors from a typical POSIX 1003.1c-1995 * Demonstrate detection of errors from a typical POSIX 1003.1c-1995
* function, pthread_join. * function, pthread_join.
*/ */
#include <errno.h>
#include <pthread.h> #include <pthread.h>
#include <stdio.h> #include <stdio.h>
#include <errno.h>
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t thread; pthread_t thread;
int status; int status;
/* /*
* Attempt to join with an uninitialized thread ID. On most * Attempt to join with an uninitialized thread ID. On most
* implementations, this will return an ESRCH error code. If * implementations, this will return an ESRCH error code. If
* the local (and uninitialized) pthread_t happens to be a valid * the local (and uninitialized) pthread_t happens to be a valid
* thread ID, it is almost certainly that of the initial thread, * thread ID, it is almost certainly that of the initial thread,
* which is running main(). In that case, your Pthreads * which is running main(). In that case, your Pthreads
* implementation may either return EDEADLK (self-deadlock), * implementation may either return EDEADLK (self-deadlock),
* or it may hang. If it hangs, quit and try again. * or it may hang. If it hangs, quit and try again.
*/ */
status = pthread_join (thread, NULL); status = pthread_join(thread, NULL);
if (status != 0) if (status != 0)
fprintf (stderr, "error %d: %s\n", status, strerror (status)); fprintf(stderr, "error %d: %s\n", status, strerror(status));
return status; return status;
} }

View File

@@ -7,22 +7,24 @@
#include <pthread.h> #include <pthread.h>
#include "errors.h" #include "errors.h"
void *hello_world (void *arg) void*
hello_world(void* arg)
{ {
printf ("Hello world\n"); printf("Hello world\n");
return NULL; return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t hello_id; pthread_t hello_id;
int status; int status;
status = pthread_create (&hello_id, NULL, hello_world, NULL); status = pthread_create(&hello_id, NULL, hello_world, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Create thread"); err_abort(status, "Create thread");
status = pthread_join (hello_id, NULL); status = pthread_join(hello_id, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Join thread"); err_abort(status, "Join thread");
return 0; return 0;
} }

View File

@@ -10,31 +10,31 @@
/* /*
* Thread start routine. * Thread start routine.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
printf("Inside thread %i\n", pthread_self()); printf("Inside thread %i\n", pthread_self());
return arg; return arg;
} }
main (int argc, char *argv[]) main(int argc, char* argv[])
{ {
pthread_t thread_id; pthread_t thread_id;
void *thread_result; void* thread_result;
int status; int status;
printf("Inside thread %i\n", pthread_self()); printf("Inside thread %i\n", pthread_self());
status = pthread_create ( status = pthread_create(&thread_id, NULL, thread_routine, NULL);
&thread_id, NULL, thread_routine, NULL); if (status != 0)
if (status != 0) err_abort(status, "Create thread");
err_abort (status, "Create thread"); sleep(1.0);
sleep(1.0); printf("Main thread created thread %i\n", thread_id);
printf("Main thread created thread %i\n", thread_id);
status = pthread_join (thread_id, &thread_result); status = pthread_join(thread_id, &thread_result);
if (status != 0) if (status != 0)
err_abort (status, "Join thread"); err_abort(status, "Join thread");
if (thread_result == NULL) if (thread_result == NULL)
return 0; return 0;
else else
return 1; return 1;
} }

View File

@@ -10,22 +10,24 @@
/* /*
* Thread start routine that writes a message. * Thread start routine that writes a message.
*/ */
void *writer_thread (void *arg) void*
writer_thread(void* arg)
{ {
sleep (5); sleep(5);
printf ("Thread I/O\n"); printf("Thread I/O\n");
return NULL; return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t writer_id; pthread_t writer_id;
char *input, buffer[64]; char *input, buffer[64];
pthread_create (&writer_id, NULL, writer_thread, NULL); pthread_create(&writer_id, NULL, writer_thread, NULL);
input = fgets (buffer, 64, stdin); input = fgets(buffer, 64, stdin);
if (input != NULL) if (input != NULL)
printf ("You said %s", buffer); printf("You said %s", buffer);
pthread_exit (NULL); pthread_exit(NULL);
return 0; return 0;
} }

View File

@@ -22,177 +22,183 @@
* been on the list. * been on the list.
*/ */
typedef struct alarm_tag { typedef struct alarm_tag {
struct alarm_tag *link; struct alarm_tag* link;
int seconds; int seconds;
time_t time; /* seconds from EPOCH */ time_t time; /* seconds from EPOCH */
char message[64]; char message[64];
} alarm_t; } alarm_t;
pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t alarm_cond = PTHREAD_COND_INITIALIZER; pthread_cond_t alarm_cond = PTHREAD_COND_INITIALIZER;
alarm_t *alarm_list = NULL; alarm_t* alarm_list = NULL;
time_t current_alarm = 0; time_t current_alarm = 0;
/* /*
* Insert alarm entry on list, in order. * Insert alarm entry on list, in order.
*/ */
void alarm_insert (alarm_t *alarm) void
alarm_insert(alarm_t* alarm)
{ {
int status; int status;
alarm_t **last, *next; alarm_t **last, *next;
/* /*
* LOCKING PROTOCOL: * LOCKING PROTOCOL:
* *
* This routine requires that the caller have locked the * This routine requires that the caller have locked the
* alarm_mutex! * alarm_mutex!
*/ */
last = &alarm_list; last = &alarm_list;
next = *last; next = *last;
while (next != NULL) { while (next != NULL) {
if (next->time >= alarm->time) { if (next->time >= alarm->time) {
alarm->link = next; alarm->link = next;
*last = alarm; *last = alarm;
break; break;
}
last = &next->link;
next = next->link;
}
/*
* If we reached the end of the list, insert the new alarm
* there. ("next" is NULL, and "last" points to the link
* field of the last item, or to the list header.)
*/
if (next == NULL) {
*last = alarm;
alarm->link = NULL;
} }
last = &next->link;
next = next->link;
}
/*
* If we reached the end of the list, insert the new alarm
* there. ("next" is NULL, and "last" points to the link
* field of the last item, or to the list header.)
*/
if (next == NULL) {
*last = alarm;
alarm->link = NULL;
}
#ifdef DEBUG #ifdef DEBUG
printf ("[list: "); printf("[list: ");
for (next = alarm_list; next != NULL; next = next->link) for (next = alarm_list; next != NULL; next = next->link)
printf ("%d(%d)[\"%s\"] ", next->time, printf(
next->time - time (NULL), next->message); "%d(%d)[\"%s\"] ", next->time, next->time - time(NULL), next->message);
printf ("]\n"); printf("]\n");
#endif #endif
/* /*
* Wake the alarm thread if it is not busy (that is, if * Wake the alarm thread if it is not busy (that is, if
* current_alarm is 0, signifying that it's waiting for * current_alarm is 0, signifying that it's waiting for
* work), or if the new alarm comes before the one on * work), or if the new alarm comes before the one on
* which the alarm thread is waiting. * which the alarm thread is waiting.
*/ */
if (current_alarm == 0 || alarm->time < current_alarm) { if (current_alarm == 0 || alarm->time < current_alarm) {
current_alarm = alarm->time; current_alarm = alarm->time;
status = pthread_cond_signal (&alarm_cond); status = pthread_cond_signal(&alarm_cond);
if (status != 0) if (status != 0)
err_abort (status, "Signal cond"); err_abort(status, "Signal cond");
} }
} }
/* /*
* The alarm thread's start routine. * The alarm thread's start routine.
*/ */
void *alarm_thread (void *arg) void*
alarm_thread(void* arg)
{ {
alarm_t *alarm; alarm_t* alarm;
struct timespec cond_time; struct timespec cond_time;
time_t now; time_t now;
int status, expired; int status, expired;
/*
* Loop forever, processing commands. The alarm thread will
* be disintegrated when the process exits. Lock the mutex
* at the start -- it will be unlocked during condition
* waits, so the main thread can insert alarms.
*/
status = pthread_mutex_lock(&alarm_mutex);
if (status != 0)
err_abort(status, "Lock mutex");
while (1) {
/*
* If the alarm list is empty, wait until an alarm is
* added. Setting current_alarm to 0 informs the insert
* routine that the thread is not busy.
*/
current_alarm = 0;
while (alarm_list == NULL) {
status = pthread_cond_wait(&alarm_cond, &alarm_mutex);
if (status != 0)
err_abort(status, "Wait on cond");
}
alarm = alarm_list;
alarm_list = alarm->link;
now = time(NULL);
expired = 0;
if (alarm->time > now) {
#ifdef DEBUG
printf("[waiting: %d(%d)\"%s\"]\n",
alarm->time,
alarm->time - time(NULL),
alarm->message);
#endif
cond_time.tv_sec = alarm->time;
cond_time.tv_nsec = 0;
current_alarm = alarm->time;
while (current_alarm == alarm->time) {
status = pthread_cond_timedwait(&alarm_cond, &alarm_mutex, &cond_time);
if (status == ETIMEDOUT) {
expired = 1;
break;
}
if (status != 0)
err_abort(status, "Cond timedwait");
}
if (!expired)
alarm_insert(alarm);
}
else
expired = 1;
if (expired) {
printf("(%d) %s\n", alarm->seconds, alarm->message);
free(alarm);
}
}
}
int
main(int argc, char* argv[])
{
int status;
char line[128];
alarm_t* alarm;
pthread_t thread;
status = pthread_create(&thread, NULL, alarm_thread, NULL);
if (status != 0)
err_abort(status, "Create alarm thread");
while (1) {
printf("Alarm> ");
if (fgets(line, sizeof(line), stdin) == NULL)
exit(0);
if (strlen(line) <= 1)
continue;
alarm = (alarm_t*) malloc(sizeof(alarm_t));
if (alarm == NULL)
errno_abort("Allocate alarm");
/* /*
* Loop forever, processing commands. The alarm thread will * Parse input line into seconds (%d) and a message
* be disintegrated when the process exits. Lock the mutex * (%64[^\n]), consisting of up to 64 characters
* at the start -- it will be unlocked during condition * separated from the seconds by whitespace.
* waits, so the main thread can insert alarms.
*/ */
status = pthread_mutex_lock (&alarm_mutex); if (sscanf(line, "%d %64[^\n]", &alarm->seconds, alarm->message) < 2) {
if (status != 0) fprintf(stderr, "Bad command\n");
err_abort (status, "Lock mutex"); free(alarm);
while (1) {
/*
* If the alarm list is empty, wait until an alarm is
* added. Setting current_alarm to 0 informs the insert
* routine that the thread is not busy.
*/
current_alarm = 0;
while (alarm_list == NULL) {
status = pthread_cond_wait (&alarm_cond, &alarm_mutex);
if (status != 0)
err_abort (status, "Wait on cond");
}
alarm = alarm_list;
alarm_list = alarm->link;
now = time (NULL);
expired = 0;
if (alarm->time > now) {
#ifdef DEBUG
printf ("[waiting: %d(%d)\"%s\"]\n", alarm->time,
alarm->time - time (NULL), alarm->message);
#endif
cond_time.tv_sec = alarm->time;
cond_time.tv_nsec = 0;
current_alarm = alarm->time;
while (current_alarm == alarm->time) {
status = pthread_cond_timedwait (
&alarm_cond, &alarm_mutex, &cond_time);
if (status == ETIMEDOUT) {
expired = 1;
break;
}
if (status != 0)
err_abort (status, "Cond timedwait");
}
if (!expired)
alarm_insert (alarm);
} else
expired = 1;
if (expired) {
printf ("(%d) %s\n", alarm->seconds, alarm->message);
free (alarm);
}
} }
} else {
status = pthread_mutex_lock(&alarm_mutex);
int main (int argc, char *argv[]) if (status != 0)
{ err_abort(status, "Lock mutex");
int status; alarm->time = time(NULL) + alarm->seconds;
char line[128]; /*
alarm_t *alarm; * Insert the new alarm into the list of alarms,
pthread_t thread; * sorted by expiration time.
*/
status = pthread_create ( alarm_insert(alarm);
&thread, NULL, alarm_thread, NULL); status = pthread_mutex_unlock(&alarm_mutex);
if (status != 0) if (status != 0)
err_abort (status, "Create alarm thread"); err_abort(status, "Unlock mutex");
while (1) {
printf ("Alarm> ");
if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
if (strlen (line) <= 1) continue;
alarm = (alarm_t*)malloc (sizeof (alarm_t));
if (alarm == NULL)
errno_abort ("Allocate alarm");
/*
* Parse input line into seconds (%d) and a message
* (%64[^\n]), consisting of up to 64 characters
* separated from the seconds by whitespace.
*/
if (sscanf (line, "%d %64[^\n]",
&alarm->seconds, alarm->message) < 2) {
fprintf (stderr, "Bad command\n");
free (alarm);
} else {
status = pthread_mutex_lock (&alarm_mutex);
if (status != 0)
err_abort (status, "Lock mutex");
alarm->time = time (NULL) + alarm->seconds;
/*
* Insert the new alarm into the list of alarms,
* sorted by expiration time.
*/
alarm_insert (alarm);
status = pthread_mutex_unlock (&alarm_mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
}
} }
}
} }

View File

@@ -22,154 +22,159 @@
* been on the list. * been on the list.
*/ */
typedef struct alarm_tag { typedef struct alarm_tag {
struct alarm_tag *link; struct alarm_tag* link;
int seconds; int seconds;
time_t time; /* seconds from EPOCH */ time_t time; /* seconds from EPOCH */
char message[64]; char message[64];
} alarm_t; } alarm_t;
pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;
alarm_t *alarm_list = NULL; alarm_t* alarm_list = NULL;
/* /*
* The alarm thread's start routine. * The alarm thread's start routine.
*/ */
void *alarm_thread (void *arg) void*
alarm_thread(void* arg)
{ {
alarm_t *alarm; alarm_t* alarm;
int sleep_time; int sleep_time;
time_t now; time_t now;
int status; int status;
/*
* Loop forever, processing commands. The alarm thread will
* be disintegrated when the process exits.
*/
while (1) {
status = pthread_mutex_lock(&alarm_mutex);
if (status != 0)
err_abort(status, "Lock mutex");
alarm = alarm_list;
/* /*
* Loop forever, processing commands. The alarm thread will * If the alarm list is empty, wait for one second. This
* be disintegrated when the process exits. * allows the main thread to run, and read another
* command. If the list is not empty, remove the first
* item. Compute the number of seconds to wait -- if the
* result is less than 0 (the time has passed), then set
* the sleep_time to 0.
*/ */
while (1) { if (alarm == NULL)
status = pthread_mutex_lock (&alarm_mutex); sleep_time = 1;
if (status != 0) else {
err_abort (status, "Lock mutex"); alarm_list = alarm->link;
alarm = alarm_list; now = time(NULL);
if (alarm->time <= now)
/* sleep_time = 0;
* If the alarm list is empty, wait for one second. This else
* allows the main thread to run, and read another sleep_time = alarm->time - now;
* command. If the list is not empty, remove the first
* item. Compute the number of seconds to wait -- if the
* result is less than 0 (the time has passed), then set
* the sleep_time to 0.
*/
if (alarm == NULL)
sleep_time = 1;
else {
alarm_list = alarm->link;
now = time (NULL);
if (alarm->time <= now)
sleep_time = 0;
else
sleep_time = alarm->time - now;
#ifdef DEBUG #ifdef DEBUG
printf ("[waiting: %d(%d)\"%s\"]\n", alarm->time, printf(
sleep_time, alarm->message); "[waiting: %d(%d)\"%s\"]\n", alarm->time, sleep_time, alarm->message);
#endif #endif
}
/*
* Unlock the mutex before waiting, so that the main
* thread can lock it to insert a new alarm request. If
* the sleep_time is 0, then call sched_yield, giving
* the main thread a chance to run if it has been
* readied by user input, without delaying the message
* if there's no input.
*/
status = pthread_mutex_unlock (&alarm_mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
if (sleep_time > 0)
sleep (sleep_time);
else
sched_yield ();
/*
* If a timer expired, print the message and free the
* structure.
*/
if (alarm != NULL) {
printf ("(%d) %s\n", alarm->seconds, alarm->message);
free (alarm);
}
} }
}
int main (int argc, char *argv[]) /*
{ * Unlock the mutex before waiting, so that the main
int status; * thread can lock it to insert a new alarm request. If
char line[128]; * the sleep_time is 0, then call sched_yield, giving
alarm_t *alarm, **last, *next; * the main thread a chance to run if it has been
pthread_t thread; * readied by user input, without delaying the message
* if there's no input.
status = pthread_create ( */
&thread, NULL, alarm_thread, NULL); status = pthread_mutex_unlock(&alarm_mutex);
if (status != 0) if (status != 0)
err_abort (status, "Create alarm thread"); err_abort(status, "Unlock mutex");
while (1) { if (sleep_time > 0)
printf ("alarm> "); sleep(sleep_time);
if (fgets (line, sizeof (line), stdin) == NULL) exit (0); else
if (strlen (line) <= 1) continue; sched_yield();
alarm = (alarm_t*)malloc (sizeof (alarm_t));
if (alarm == NULL)
errno_abort ("Allocate alarm");
/* /*
* Parse input line into seconds (%d) and a message * If a timer expired, print the message and free the
* (%64[^\n]), consisting of up to 64 characters * structure.
* separated from the seconds by whitespace. */
*/ if (alarm != NULL) {
if (sscanf (line, "%d %64[^\n]", printf("(%d) %s\n", alarm->seconds, alarm->message);
&alarm->seconds, alarm->message) < 2) { free(alarm);
fprintf (stderr, "Bad command\n");
free (alarm);
} else {
status = pthread_mutex_lock (&alarm_mutex);
if (status != 0)
err_abort (status, "Lock mutex");
alarm->time = time (NULL) + alarm->seconds;
/*
* Insert the new alarm into the list of alarms,
* sorted by expiration time.
*/
last = &alarm_list;
next = *last;
while (next != NULL) {
if (next->time >= alarm->time) {
alarm->link = next;
*last = alarm;
break;
}
last = &next->link;
next = next->link;
}
/*
* If we reached the end of the list, insert the new
* alarm there. ("next" is NULL, and "last" points
* to the link field of the last item, or to the
* list header).
*/
if (next == NULL) {
*last = alarm;
alarm->link = NULL;
}
#ifdef DEBUG
printf ("[list: ");
for (next = alarm_list; next != NULL; next = next->link)
printf ("%d(%d)[\"%s\"] ", next->time,
next->time - time (NULL), next->message);
printf ("]\n");
#endif
status = pthread_mutex_unlock (&alarm_mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
}
} }
}
}
int
main(int argc, char* argv[])
{
int status;
char line[128];
alarm_t *alarm, **last, *next;
pthread_t thread;
status = pthread_create(&thread, NULL, alarm_thread, NULL);
if (status != 0)
err_abort(status, "Create alarm thread");
while (1) {
printf("alarm> ");
if (fgets(line, sizeof(line), stdin) == NULL)
exit(0);
if (strlen(line) <= 1)
continue;
alarm = (alarm_t*) malloc(sizeof(alarm_t));
if (alarm == NULL)
errno_abort("Allocate alarm");
/*
* Parse input line into seconds (%d) and a message
* (%64[^\n]), consisting of up to 64 characters
* separated from the seconds by whitespace.
*/
if (sscanf(line, "%d %64[^\n]", &alarm->seconds, alarm->message) < 2) {
fprintf(stderr, "Bad command\n");
free(alarm);
}
else {
status = pthread_mutex_lock(&alarm_mutex);
if (status != 0)
err_abort(status, "Lock mutex");
alarm->time = time(NULL) + alarm->seconds;
/*
* Insert the new alarm into the list of alarms,
* sorted by expiration time.
*/
last = &alarm_list;
next = *last;
while (next != NULL) {
if (next->time >= alarm->time) {
alarm->link = next;
*last = alarm;
break;
}
last = &next->link;
next = next->link;
}
/*
* If we reached the end of the list, insert the new
* alarm there. ("next" is NULL, and "last" points
* to the link field of the last item, or to the
* list header).
*/
if (next == NULL) {
*last = alarm;
alarm->link = NULL;
}
#ifdef DEBUG
printf("[list: ");
for (next = alarm_list; next != NULL; next = next->link)
printf("%d(%d)[\"%s\"] ",
next->time,
next->time - time(NULL),
next->message);
printf("]\n");
#endif
status = pthread_mutex_unlock(&alarm_mutex);
if (status != 0)
err_abort(status, "Unlock mutex");
}
}
} }

View File

@@ -39,7 +39,9 @@ std::list<alarm_t> alarm_list;
/* /*
* The alarm thread's start routine. * The alarm thread's start routine.
*/ */
void *alarm_thread(void *arg) { void*
alarm_thread(void* arg)
{
int sleep_time; int sleep_time;
time_t now; time_t now;
int status; int status;
@@ -50,16 +52,18 @@ void *alarm_thread(void *arg) {
*/ */
while (1) { while (1) {
status = pthread_mutex_lock(&alarm_mutex); status = pthread_mutex_lock(&alarm_mutex);
if (status != 0) err_abort(status, "Lock mutex"); if (status != 0)
err_abort(status, "Lock mutex");
now = time(NULL); now = time(NULL);
sleep_time = 0; sleep_time = 0;
for (auto it = alarm_list.begin(); it != alarm_list.end();) { for (auto it = alarm_list.begin(); it != alarm_list.end();) {
if (it->time <= now) { if (it->time <= now) {
printf("(%d) %s\n", it->seconds, it->message); printf("(%d) %s\n", it->seconds, it->message);
it = alarm_list.erase(it); it = alarm_list.erase(it);
} else { }
else {
sleep_time = MIN(it->time - now, sleep_time); sleep_time = MIN(it->time - now, sleep_time);
++it; ++it;
} }
@@ -74,7 +78,8 @@ void *alarm_thread(void *arg) {
* if there's no input. * if there's no input.
*/ */
status = pthread_mutex_unlock(&alarm_mutex); status = pthread_mutex_unlock(&alarm_mutex);
if (status != 0) err_abort(status, "Unlock mutex"); if (status != 0)
err_abort(status, "Unlock mutex");
if (sleep_time > 0) if (sleep_time > 0)
sleep(sleep_time); sleep(sleep_time);
else else
@@ -82,17 +87,22 @@ void *alarm_thread(void *arg) {
} }
} }
int main(int argc, char *argv[]) { int
main(int argc, char* argv[])
{
int status; int status;
char line[128]; char line[128];
pthread_t thread; pthread_t thread;
status = pthread_create(&thread, NULL, alarm_thread, NULL); status = pthread_create(&thread, NULL, alarm_thread, NULL);
if (status != 0) err_abort(status, "Create alarm thread"); if (status != 0)
err_abort(status, "Create alarm thread");
while (1) { while (1) {
printf("alarm> "); printf("alarm> ");
if (fgets(line, sizeof(line), stdin) == NULL) exit(0); if (fgets(line, sizeof(line), stdin) == NULL)
if (strlen(line) <= 1) continue; exit(0);
if (strlen(line) <= 1)
continue;
alarm_t alarm; alarm_t alarm;
/* /*
@@ -102,11 +112,13 @@ int main(int argc, char *argv[]) {
*/ */
if (sscanf(line, "%d %64[^\n]", &alarm.seconds, alarm.message) < 2) { if (sscanf(line, "%d %64[^\n]", &alarm.seconds, alarm.message) < 2) {
fprintf(stderr, "Bad command\n"); fprintf(stderr, "Bad command\n");
} else { }
else {
alarm.time = time(NULL) + alarm.seconds; alarm.time = time(NULL) + alarm.seconds;
status = pthread_mutex_lock(&alarm_mutex); status = pthread_mutex_lock(&alarm_mutex);
if (status != 0) err_abort(status, "Lock mutex"); if (status != 0)
err_abort(status, "Lock mutex");
/* /*
* Insert the new alarm into the list of alarms. * Insert the new alarm into the list of alarms.
@@ -115,13 +127,16 @@ int main(int argc, char *argv[]) {
#ifdef DEBUG #ifdef DEBUG
printf("[list: "); printf("[list: ");
for (const auto &iAlarm : alarm_list) for (const auto& iAlarm : alarm_list)
printf("%d(%d)[\"%s\"] ", iAlarm.time, iAlarm.time - time(NULL), printf("%d(%d)[\"%s\"] ",
iAlarm.time,
iAlarm.time - time(NULL),
iAlarm.message); iAlarm.message);
printf("]\n"); printf("]\n");
#endif #endif
status = pthread_mutex_unlock(&alarm_mutex); status = pthread_mutex_unlock(&alarm_mutex);
if (status != 0) err_abort(status, "Unlock mutex"); if (status != 0)
err_abort(status, "Unlock mutex");
} }
} }
} }

View File

@@ -17,75 +17,73 @@
/* /*
* Initialize a static array of 3 mutexes. * Initialize a static array of 3 mutexes.
*/ */
pthread_mutex_t mutex[3] = { pthread_mutex_t mutex[3] = {PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER};
PTHREAD_MUTEX_INITIALIZER
};
int backoff = 1; /* Whether to backoff or deadlock */ int backoff = 1; /* Whether to backoff or deadlock */
int yield_flag = 0; /* 0: no yield, >0: yield, <0: sleep */ int yield_flag = 0; /* 0: no yield, >0: yield, <0: sleep */
/* /*
* This is a thread start routine that locks all mutexes in * This is a thread start routine that locks all mutexes in
* order, to ensure a conflict with lock_reverse, which does the * order, to ensure a conflict with lock_reverse, which does the
* opposite. * opposite.
*/ */
void *lock_forward (void *arg) void*
lock_forward(void* arg)
{ {
int i, iterate, backoffs; int i, iterate, backoffs;
int status; int status;
for (iterate = 0; iterate < ITERATIONS; iterate++) { for (iterate = 0; iterate < ITERATIONS; iterate++) {
backoffs = 0; backoffs = 0;
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
if (i == 0) { if (i == 0) {
status = pthread_mutex_lock (&mutex[i]); status = pthread_mutex_lock(&mutex[i]);
if (status != 0) if (status != 0)
err_abort (status, "First lock"); err_abort(status, "First lock");
} else { }
if (backoff) else {
status = pthread_mutex_trylock (&mutex[i]); if (backoff)
else status = pthread_mutex_trylock(&mutex[i]);
status = pthread_mutex_lock (&mutex[i]); else
if (status == EBUSY) { status = pthread_mutex_lock(&mutex[i]);
backoffs++; if (status == EBUSY) {
DPRINTF (( backoffs++;
" [forward locker backing off at %d]\n", DPRINTF((" [forward locker backing off at %d]\n", i));
i)); for (; i >= 0; i--) {
for (; i >= 0; i--) { status = pthread_mutex_unlock(&mutex[i]);
status = pthread_mutex_unlock (&mutex[i]); if (status != 0)
if (status != 0) err_abort(status, "Backoff");
err_abort (status, "Backoff"); }
}
} else {
if (status != 0)
err_abort (status, "Lock mutex");
DPRINTF ((" forward locker got %d\n", i));
}
}
/*
* Yield processor, if needed to be sure locks get
* interleaved on a uniprocessor.
*/
if (yield_flag) {
if (yield_flag > 0)
sched_yield ();
else
sleep (1);
}
} }
/* else {
* Report that we got 'em, and unlock to try again. if (status != 0)
*/ err_abort(status, "Lock mutex");
printf ( DPRINTF((" forward locker got %d\n", i));
"lock forward got all locks, %d backoffs\n", backoffs); }
pthread_mutex_unlock (&mutex[2]); }
pthread_mutex_unlock (&mutex[1]); /*
pthread_mutex_unlock (&mutex[0]); * Yield processor, if needed to be sure locks get
sched_yield (); * interleaved on a uniprocessor.
*/
if (yield_flag) {
if (yield_flag > 0)
sched_yield();
else
sleep(1);
}
} }
return NULL; /*
* Report that we got 'em, and unlock to try again.
*/
printf("lock forward got all locks, %d backoffs\n", backoffs);
pthread_mutex_unlock(&mutex[2]);
pthread_mutex_unlock(&mutex[1]);
pthread_mutex_unlock(&mutex[0]);
sched_yield();
}
return NULL;
} }
/* /*
@@ -93,107 +91,106 @@ void *lock_forward (void *arg)
* reverse order, to ensure a conflict with lock_forward, which * reverse order, to ensure a conflict with lock_forward, which
* does the opposite. * does the opposite.
*/ */
void *lock_backward (void *arg) void*
lock_backward(void* arg)
{ {
int i, iterate, backoffs; int i, iterate, backoffs;
int status; int status;
for (iterate = 0; iterate < ITERATIONS; iterate++) { for (iterate = 0; iterate < ITERATIONS; iterate++) {
backoffs = 0; backoffs = 0;
for (i = 2; i >= 0; i--) { for (i = 2; i >= 0; i--) {
if (i == 2) { if (i == 2) {
status = pthread_mutex_lock (&mutex[i]); status = pthread_mutex_lock(&mutex[i]);
if (status != 0) if (status != 0)
err_abort (status, "First lock"); err_abort(status, "First lock");
} else { }
if (backoff) else {
status = pthread_mutex_trylock (&mutex[i]); if (backoff)
else status = pthread_mutex_trylock(&mutex[i]);
status = pthread_mutex_lock (&mutex[i]); else
if (status == EBUSY) { status = pthread_mutex_lock(&mutex[i]);
backoffs++; if (status == EBUSY) {
DPRINTF (( backoffs++;
" [backward locker backing off at %d]\n", DPRINTF((" [backward locker backing off at %d]\n", i));
i)); for (; i < 3; i++) {
for (; i < 3; i++) { status = pthread_mutex_unlock(&mutex[i]);
status = pthread_mutex_unlock (&mutex[i]); if (status != 0)
if (status != 0) err_abort(status, "Backoff");
err_abort (status, "Backoff"); }
}
} else {
if (status != 0)
err_abort (status, "Lock mutex");
DPRINTF ((" backward locker got %d\n", i));
}
}
/*
* Yield processor, if needed to be sure locks get
* interleaved on a uniprocessor.
*/
if (yield_flag) {
if (yield_flag > 0)
sched_yield ();
else
sleep (1);
}
} }
/* else {
* Report that we got 'em, and unlock to try again. if (status != 0)
*/ err_abort(status, "Lock mutex");
printf ( DPRINTF((" backward locker got %d\n", i));
"lock backward got all locks, %d backoffs\n", backoffs); }
pthread_mutex_unlock (&mutex[0]); }
pthread_mutex_unlock (&mutex[1]); /*
pthread_mutex_unlock (&mutex[2]); * Yield processor, if needed to be sure locks get
sched_yield (); * interleaved on a uniprocessor.
*/
if (yield_flag) {
if (yield_flag > 0)
sched_yield();
else
sleep(1);
}
} }
return NULL; /*
* Report that we got 'em, and unlock to try again.
*/
printf("lock backward got all locks, %d backoffs\n", backoffs);
pthread_mutex_unlock(&mutex[0]);
pthread_mutex_unlock(&mutex[1]);
pthread_mutex_unlock(&mutex[2]);
sched_yield();
}
return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t forward, backward; pthread_t forward, backward;
int status; int status;
#ifdef sun #ifdef sun
/* /*
* On Solaris 2.5, threads are not timesliced. To ensure * On Solaris 2.5, threads are not timesliced. To ensure
* that our threads can run concurrently, we need to * that our threads can run concurrently, we need to
* increase the concurrency level. * increase the concurrency level.
*/ */
DPRINTF (("Setting concurrency level to 2\n")); DPRINTF(("Setting concurrency level to 2\n"));
thr_setconcurrency (2); thr_setconcurrency(2);
#endif #endif
/* /*
* If the first argument is absent, or nonzero, a backoff * If the first argument is absent, or nonzero, a backoff
* algorithm will be used to avoid deadlock. If the first * algorithm will be used to avoid deadlock. If the first
* argument is zero, the program will deadlock on a lock * argument is zero, the program will deadlock on a lock
* "collision." * "collision."
*/ */
if (argc > 1) if (argc > 1)
backoff = atoi (argv[1]); backoff = atoi(argv[1]);
/* /*
* If the second argument is absent, or zero, the two * If the second argument is absent, or zero, the two
* threads run "at speed." On some systems, especially * threads run "at speed." On some systems, especially
* uniprocessors, one thread may complete before the other * uniprocessors, one thread may complete before the other
* has a chance to run, and you won't see a deadlock or * has a chance to run, and you won't see a deadlock or
* backoffs. In that case, try running with the argument set * backoffs. In that case, try running with the argument set
* to a positive number to cause the threads to call * to a positive number to cause the threads to call
* sched_yield() at each lock; or, to make it even more * sched_yield() at each lock; or, to make it even more
* obvious, set to a negative number to cause the threads to * obvious, set to a negative number to cause the threads to
* call sleep(1) instead. * call sleep(1) instead.
*/ */
if (argc > 2) if (argc > 2)
yield_flag = atoi (argv[2]); yield_flag = atoi(argv[2]);
status = pthread_create ( status = pthread_create(&forward, NULL, lock_forward, NULL);
&forward, NULL, lock_forward, NULL); if (status != 0)
if (status != 0) err_abort(status, "Create forward");
err_abort (status, "Create forward"); status = pthread_create(&backward, NULL, lock_backward, NULL);
status = pthread_create ( if (status != 0)
&backward, NULL, lock_backward, NULL); err_abort(status, "Create backward");
if (status != 0) pthread_exit(NULL);
err_abort (status, "Create backward");
pthread_exit (NULL);
} }

View File

@@ -8,88 +8,87 @@
#include "errors.h" #include "errors.h"
typedef struct my_struct_tag { typedef struct my_struct_tag {
pthread_mutex_t mutex; /* Protects access to value */ pthread_mutex_t mutex; /* Protects access to value */
pthread_cond_t cond; /* Signals change to value */ pthread_cond_t cond; /* Signals change to value */
int value; /* Access protected by mutex */ int value; /* Access protected by mutex */
} my_struct_t; } my_struct_t;
my_struct_t data = { my_struct_t data = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
int hibernation = 1; /* Default to 1 second */ int hibernation = 1; /* Default to 1 second */
/* /*
* Thread start routine. It will set the main thread's predicate * Thread start routine. It will set the main thread's predicate
* and signal the condition variable. * and signal the condition variable.
*/ */
void * void*
wait_thread (void *arg) wait_thread(void* arg)
{ {
int status; int status;
sleep (hibernation); sleep(hibernation);
status = pthread_mutex_lock (&data.mutex); status = pthread_mutex_lock(&data.mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock mutex"); err_abort(status, "Lock mutex");
data.value = 1; /* Set predicate */ data.value = 1; /* Set predicate */
status = pthread_cond_signal (&data.cond); status = pthread_cond_signal(&data.cond);
if (status != 0) if (status != 0)
err_abort (status, "Signal condition"); err_abort(status, "Signal condition");
status = pthread_mutex_unlock (&data.mutex); status = pthread_mutex_unlock(&data.mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock mutex"); err_abort(status, "Unlock mutex");
return NULL; return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
int status; int status;
pthread_t wait_thread_id; pthread_t wait_thread_id;
struct timespec timeout; struct timespec timeout;
/* /*
* If an argument is specified, interpret it as the number * If an argument is specified, interpret it as the number
* of seconds for wait_thread to sleep before signaling the * of seconds for wait_thread to sleep before signaling the
* condition variable. You can play with this to see the * condition variable. You can play with this to see the
* condition wait below time out or wake normally. * condition wait below time out or wake normally.
*/ */
if (argc > 1) if (argc > 1)
hibernation = atoi (argv[1]); hibernation = atoi(argv[1]);
/* /*
* Create wait_thread. * Create wait_thread.
*/ */
status = pthread_create (&wait_thread_id, NULL, wait_thread, NULL); status = pthread_create(&wait_thread_id, NULL, wait_thread, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Create wait thread"); err_abort(status, "Create wait thread");
/* /*
* Wait on the condition variable for 2 seconds, or until * Wait on the condition variable for 2 seconds, or until
* signaled by the wait_thread. Normally, wait_thread * signaled by the wait_thread. Normally, wait_thread
* should signal. If you raise "hibernation" above 2 * should signal. If you raise "hibernation" above 2
* seconds, it will time out. * seconds, it will time out.
*/ */
timeout.tv_sec = time (NULL) + 2; timeout.tv_sec = time(NULL) + 2;
timeout.tv_nsec = 0; timeout.tv_nsec = 0;
status = pthread_mutex_lock (&data.mutex); status = pthread_mutex_lock(&data.mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock mutex"); err_abort(status, "Lock mutex");
while (data.value == 0) { while (data.value == 0) {
status = pthread_cond_timedwait ( status = pthread_cond_timedwait(&data.cond, &data.mutex, &timeout);
&data.cond, &data.mutex, &timeout); if (status == ETIMEDOUT) {
if (status == ETIMEDOUT) { printf("Condition wait timed out.\n");
printf ("Condition wait timed out.\n"); break;
break;
}
else if (status != 0)
err_abort (status, "Wait on condition");
} }
else if (status != 0)
err_abort(status, "Wait on condition");
}
if (data.value != 0) if (data.value != 0)
printf ("Condition was signaled.\n"); printf("Condition was signaled.\n");
status = pthread_mutex_unlock (&data.mutex); status = pthread_mutex_unlock(&data.mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock mutex"); err_abort(status, "Unlock mutex");
return 0; return 0;
} }

View File

@@ -10,31 +10,32 @@
* Define a structure, with a mutex and condition variable. * Define a structure, with a mutex and condition variable.
*/ */
typedef struct my_struct_tag { typedef struct my_struct_tag {
pthread_mutex_t mutex; /* Protects access to value */ pthread_mutex_t mutex; /* Protects access to value */
pthread_cond_t cond; /* Signals change to value */ pthread_cond_t cond; /* Signals change to value */
int value; /* Access protected by mutex */ int value; /* Access protected by mutex */
} my_struct_t; } my_struct_t;
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
my_struct_t *data; my_struct_t* data;
int status; int status;
data = malloc (sizeof (my_struct_t)); data = malloc(sizeof(my_struct_t));
if (data == NULL) if (data == NULL)
errno_abort ("Allocate structure"); errno_abort("Allocate structure");
status = pthread_mutex_init (&data->mutex, NULL); status = pthread_mutex_init(&data->mutex, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Init mutex"); err_abort(status, "Init mutex");
status = pthread_cond_init (&data->cond, NULL); status = pthread_cond_init(&data->cond, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Init condition"); err_abort(status, "Init condition");
status = pthread_cond_destroy (&data->cond); status = pthread_cond_destroy(&data->cond);
if (status != 0) if (status != 0)
err_abort (status, "Destroy condition"); err_abort(status, "Destroy condition");
status = pthread_mutex_destroy (&data->mutex); status = pthread_mutex_destroy(&data->mutex);
if (status != 0) if (status != 0)
err_abort (status, "Destroy mutex"); err_abort(status, "Destroy mutex");
(void)free (data); (void) free(data);
return status; return status;
} }

View File

@@ -13,15 +13,15 @@
* attributes. * attributes.
*/ */
typedef struct my_struct_tag { typedef struct my_struct_tag {
pthread_mutex_t mutex; /* Protects access to value */ pthread_mutex_t mutex; /* Protects access to value */
pthread_cond_t cond; /* Signals change to value */ pthread_cond_t cond; /* Signals change to value */
int value; /* Access protected by mutex */ int value; /* Access protected by mutex */
} my_struct_t; } my_struct_t;
my_struct_t data = { my_struct_t data = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
return 0; return 0;
} }

View File

@@ -10,24 +10,25 @@
* Define a structure, with a mutex. * Define a structure, with a mutex.
*/ */
typedef struct my_struct_tag { typedef struct my_struct_tag {
pthread_mutex_t mutex; /* Protects access to value */ pthread_mutex_t mutex; /* Protects access to value */
int value; /* Access protected by mutex */ int value; /* Access protected by mutex */
} my_struct_t; } my_struct_t;
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
my_struct_t *data; my_struct_t* data;
int status; int status;
data = malloc (sizeof (my_struct_t)); data = malloc(sizeof(my_struct_t));
if (data == NULL) if (data == NULL)
errno_abort ("Allocate structure"); errno_abort("Allocate structure");
status = pthread_mutex_init (&data->mutex, NULL); status = pthread_mutex_init(&data->mutex, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Init mutex"); err_abort(status, "Init mutex");
status = pthread_mutex_destroy (&data->mutex); status = pthread_mutex_destroy(&data->mutex);
if (status != 0) if (status != 0)
err_abort (status, "Destroy mutex"); err_abort(status, "Destroy mutex");
(void)free (data); (void) free(data);
return status; return status;
} }

View File

@@ -11,13 +11,14 @@
* same as using pthread_mutex_init, with the default attributes. * same as using pthread_mutex_init, with the default attributes.
*/ */
typedef struct my_struct_tag { typedef struct my_struct_tag {
pthread_mutex_t mutex; /* Protects access to value */ pthread_mutex_t mutex; /* Protects access to value */
int value; /* Access protected by mutex */ int value; /* Access protected by mutex */
} my_struct_t; } my_struct_t;
my_struct_t data = {PTHREAD_MUTEX_INITIALIZER, 0}; my_struct_t data = {PTHREAD_MUTEX_INITIALIZER, 0};
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
return 0; return 0;
} }

View File

@@ -24,7 +24,9 @@ time_t end_time;
* Thread start routine that repeatedly locks a mutex and * Thread start routine that repeatedly locks a mutex and
* increments a counter. * increments a counter.
*/ */
void *counter_thread(void *arg) { void*
counter_thread(void* arg)
{
int status; int status;
int spin; int spin;
@@ -36,11 +38,13 @@ void *counter_thread(void *arg) {
*/ */
while (time(NULL) < end_time) { while (time(NULL) < end_time) {
status = pthread_mutex_lock(&mutex); status = pthread_mutex_lock(&mutex);
if (status != 0) err_abort(status, "Lock mutex"); if (status != 0)
err_abort(status, "Lock mutex");
for (spin = 0; spin < SPIN; spin++) counter++; for (spin = 0; spin < SPIN; spin++) counter++;
sleep(4); sleep(4);
status = pthread_mutex_unlock(&mutex); status = pthread_mutex_unlock(&mutex);
if (status != 0) err_abort(status, "Unlock mutex"); if (status != 0)
err_abort(status, "Unlock mutex");
sleep(1); sleep(1);
} }
printf("Counter is %#lx\n", counter); printf("Counter is %#lx\n", counter);
@@ -52,7 +56,9 @@ void *counter_thread(void *arg) {
* seconds, try to lock the mutex and read the counter. If the * seconds, try to lock the mutex and read the counter. If the
* trylock fails, skip this cycle. * trylock fails, skip this cycle.
*/ */
void *monitor_thread(void *arg) { void*
monitor_thread(void* arg)
{
int status; int status;
int misses = 0; int misses = 0;
@@ -64,18 +70,23 @@ void *monitor_thread(void *arg) {
sleep(3); sleep(3);
status = pthread_mutex_trylock(&mutex); status = pthread_mutex_trylock(&mutex);
if (status != EBUSY) { if (status != EBUSY) {
if (status != 0) err_abort(status, "Trylock mutex"); if (status != 0)
err_abort(status, "Trylock mutex");
printf("Counter is %ld\n", counter / SPIN); printf("Counter is %ld\n", counter / SPIN);
status = pthread_mutex_unlock(&mutex); status = pthread_mutex_unlock(&mutex);
if (status != 0) err_abort(status, "Unlock mutex"); if (status != 0)
} else err_abort(status, "Unlock mutex");
}
else
misses++; /* Count "misses" on the lock */ misses++; /* Count "misses" on the lock */
} }
printf("Monitor thread missed update %d times.\n", misses); printf("Monitor thread missed update %d times.\n", misses);
return NULL; return NULL;
} }
int main(int argc, char *argv[]) { int
main(int argc, char* argv[])
{
int status; int status;
pthread_t counter_thread_id; pthread_t counter_thread_id;
pthread_t monitor_thread_id; pthread_t monitor_thread_id;
@@ -91,13 +102,17 @@ int main(int argc, char *argv[]) {
#endif #endif
end_time = time(NULL) + 60; /* Run for 1 minute */ end_time = time(NULL) + 60; /* Run for 1 minute */
status = pthread_create(&counter_thread_id, NULL, counter_thread, NULL); status = pthread_create(&counter_thread_id, NULL, counter_thread, NULL);
if (status != 0) err_abort(status, "Create counter thread"); if (status != 0)
err_abort(status, "Create counter thread");
status = pthread_create(&monitor_thread_id, NULL, monitor_thread, NULL); status = pthread_create(&monitor_thread_id, NULL, monitor_thread, NULL);
if (status != 0) err_abort(status, "Create monitor thread"); if (status != 0)
err_abort(status, "Create monitor thread");
status = pthread_join(counter_thread_id, NULL); status = pthread_join(counter_thread_id, NULL);
if (status != 0) err_abort(status, "Join counter thread"); if (status != 0)
err_abort(status, "Join counter thread");
status = pthread_join(monitor_thread_id, NULL); status = pthread_join(monitor_thread_id, NULL);
if (status != 0) err_abort(status, "Join monitor thread"); if (status != 0)
err_abort(status, "Join monitor thread");
return 0; return 0;
} }

View File

@@ -9,22 +9,22 @@
* by calling thr_setconcurrency(), because threads are not * by calling thr_setconcurrency(), because threads are not
* timesliced. * timesliced.
*/ */
#include <sys/types.h> #include <dirent.h>
#include <pthread.h> #include <pthread.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <dirent.h> #include <sys/types.h>
#include "errors.h" #include "errors.h"
#define CREW_SIZE 4 #define CREW_SIZE 4
/* /*
* Queued items of work for the crew. One is queued by * Queued items of work for the crew. One is queued by
* crew_start, and each worker may queue additional items. * crew_start, and each worker may queue additional items.
*/ */
typedef struct work_tag { typedef struct work_tag {
struct work_tag *next; /* Next work item */ struct work_tag* next; /* Next work item */
char *path; /* Directory or file */ char* path; /* Directory or file */
char *string; /* Search string */ char* string; /* Search string */
} work_t, *work_p; } work_t, *work_p;
/* /*
@@ -32,9 +32,9 @@ typedef struct work_tag {
* crew. It contains the "identity" of each worker. * crew. It contains the "identity" of each worker.
*/ */
typedef struct worker_tag { typedef struct worker_tag {
int index; /* Thread's index */ int index; /* Thread's index */
pthread_t thread; /* Thread for stage */ pthread_t thread; /* Thread for stage */
struct crew_tag *crew; /* Pointer to crew */ struct crew_tag* crew; /* Pointer to crew */
} worker_t, *worker_p; } worker_t, *worker_p;
/* /*
@@ -42,444 +42,459 @@ typedef struct worker_tag {
* crew synchronization state and staging area. * crew synchronization state and staging area.
*/ */
typedef struct crew_tag { typedef struct crew_tag {
int crew_size; /* Size of array */ int crew_size; /* Size of array */
worker_t crew[CREW_SIZE];/* Crew members */ worker_t crew[CREW_SIZE]; /* Crew members */
long work_count; /* Count of work items */ long work_count; /* Count of work items */
work_t *first, *last; /* First & last work item */ work_t *first, *last; /* First & last work item */
pthread_mutex_t mutex; /* Mutex for crew data */ pthread_mutex_t mutex; /* Mutex for crew data */
pthread_cond_t done; /* Wait for crew done */ pthread_cond_t done; /* Wait for crew done */
pthread_cond_t go; /* Wait for work */ pthread_cond_t go; /* Wait for work */
} crew_t, *crew_p; } crew_t, *crew_p;
size_t path_max; /* Filepath length */ size_t path_max; /* Filepath length */
size_t name_max; /* Name length */ size_t name_max; /* Name length */
/* /*
* The thread start routine for crew threads. Waits until "go" * The thread start routine for crew threads. Waits until "go"
* command, processes work items until requested to shut down. * command, processes work items until requested to shut down.
*/ */
void *worker_routine (void *arg) void*
worker_routine(void* arg)
{ {
worker_p mine = (worker_t*)arg; worker_p mine = (worker_t*) arg;
crew_p crew = mine->crew; crew_p crew = mine->crew;
work_p work, new_work; work_p work, new_work;
struct stat filestat; struct stat filestat;
struct dirent *entry; struct dirent* entry;
int status; int status;
/* /*
* "struct dirent" is funny, because POSIX doesn't require * "struct dirent" is funny, because POSIX doesn't require
* the definition to be more than a header for a variable * the definition to be more than a header for a variable
* buffer. Thus, allocate a "big chunk" of memory, and use * buffer. Thus, allocate a "big chunk" of memory, and use
* it as a buffer. * it as a buffer.
*/ */
entry = (struct dirent*)malloc ( entry = (struct dirent*) malloc(sizeof(struct dirent) + name_max);
sizeof (struct dirent) + name_max); if (entry == NULL)
if (entry == NULL) errno_abort("Allocating dirent");
errno_abort ("Allocating dirent");
status = pthread_mutex_lock(&crew->mutex);
status = pthread_mutex_lock (&crew->mutex); if (status != 0)
err_abort(status, "Lock crew mutex");
/*
* There won't be any work when the crew is created, so wait
* until something's put on the queue.
*/
while (crew->work_count == 0) {
status = pthread_cond_wait(&crew->go, &crew->mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock crew mutex"); err_abort(status, "Wait for go");
}
status = pthread_mutex_unlock(&crew->mutex);
if (status != 0)
err_abort(status, "Unlock mutex");
DPRINTF(("Crew %d starting\n", mine->index));
/*
* Now, as long as there's work, keep doing it.
*/
while (1) {
/* /*
* There won't be any work when the crew is created, so wait * Wait while there is nothing to do, and
* until something's put on the queue. * the hope of something coming along later. If
* crew->first is NULL, there's no work. But if
* crew->work_count goes to zero, we're done.
*/ */
while (crew->work_count == 0) { status = pthread_mutex_lock(&crew->mutex);
status = pthread_cond_wait (&crew->go, &crew->mutex); if (status != 0)
if (status != 0) err_abort(status, "Lock crew mutex");
err_abort (status, "Wait for go");
DPRINTF(("Crew %d top: first is %#lx, count is %d\n",
mine->index,
crew->first,
crew->work_count));
while (crew->first == NULL) {
status = pthread_cond_wait(&crew->go, &crew->mutex);
if (status != 0)
err_abort(status, "Wait for work");
} }
status = pthread_mutex_unlock (&crew->mutex); DPRINTF(("Crew %d woke: %#lx, %d\n",
if (status != 0) mine->index,
err_abort (status, "Unlock mutex"); crew->first,
crew->work_count));
DPRINTF (("Crew %d starting\n", mine->index));
/* /*
* Now, as long as there's work, keep doing it. * Remove and process a work item
*/ */
while (1) { work = crew->first;
/* crew->first = work->next;
* Wait while there is nothing to do, and if (crew->first == NULL)
* the hope of something coming along later. If crew->last = NULL;
* crew->first is NULL, there's no work. But if
* crew->work_count goes to zero, we're done.
*/
status = pthread_mutex_lock (&crew->mutex);
if (status != 0)
err_abort (status, "Lock crew mutex");
DPRINTF (("Crew %d top: first is %#lx, count is %d\n", DPRINTF(("Crew %d took %#lx, leaves first %#lx, last %#lx\n",
mine->index, crew->first, crew->work_count)); mine->index,
while (crew->first == NULL) { work,
status = pthread_cond_wait (&crew->go, &crew->mutex); crew->first,
if (status != 0) crew->last));
err_abort (status, "Wait for work");
status = pthread_mutex_unlock(&crew->mutex);
if (status != 0)
err_abort(status, "Unlock mutex");
/*
* We have a work item. Process it, which may involve
* queuing new work items.
*/
status = lstat(work->path, &filestat);
if (S_ISLNK(filestat.st_mode))
printf("Thread %d: %s is a link, skipping.\n", mine->index, work->path);
else if (S_ISDIR(filestat.st_mode)) {
DIR* directory;
struct dirent* result;
/*
* If the file is a directory, search it and place
* all files onto the queue as new work items.
*/
directory = opendir(work->path);
if (directory == NULL) {
fprintf(stderr,
"Unable to open directory %s: %d (%s)\n",
work->path,
errno,
strerror(errno));
continue;
}
while (1) {
status = readdir_r(directory, entry, &result);
if (status != 0) {
fprintf(stderr,
"Unable to read directory %s: %d (%s)\n",
work->path,
status,
strerror(status));
break;
} }
if (result == NULL)
DPRINTF (("Crew %d woke: %#lx, %d\n", break; /* End of directory */
mine->index, crew->first, crew->work_count));
/* /*
* Remove and process a work item * Ignore "." and ".." entries.
*/ */
work = crew->first; if (strcmp(entry->d_name, ".") == 0)
crew->first = work->next; continue;
if (crew->first == NULL) if (strcmp(entry->d_name, "..") == 0)
crew->last = NULL; continue;
new_work = (work_p) malloc(sizeof(work_t));
DPRINTF (("Crew %d took %#lx, leaves first %#lx, last %#lx\n", if (new_work == NULL)
mine->index, work, crew->first, crew->last)); errno_abort("Unable to allocate space");
new_work->path = (char*) malloc(path_max);
status = pthread_mutex_unlock (&crew->mutex); if (new_work->path == NULL)
errno_abort("Unable to allocate path");
strcpy(new_work->path, work->path);
strcat(new_work->path, "/");
strcat(new_work->path, entry->d_name);
new_work->string = work->string;
new_work->next = NULL;
status = pthread_mutex_lock(&crew->mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock mutex"); err_abort(status, "Lock mutex");
if (crew->first == NULL) {
crew->first = new_work;
crew->last = new_work;
}
else {
crew->last->next = new_work;
crew->last = new_work;
}
crew->work_count++;
DPRINTF(("Crew %d: add work %#lx, first %#lx, last %#lx, %d\n",
mine->index,
new_work,
crew->first,
crew->last,
crew->work_count));
status = pthread_cond_signal(&crew->go);
status = pthread_mutex_unlock(&crew->mutex);
if (status != 0)
err_abort(status, "Unlock mutex");
}
/* closedir(directory);
* We have a work item. Process it, which may involve }
* queuing new work items. else if (S_ISREG(filestat.st_mode)) {
*/ FILE* search;
status = lstat (work->path, &filestat); char buffer[256], *bufptr, *search_ptr;
if (S_ISLNK (filestat.st_mode)) /*
printf ( * If this is a file, not a directory, then search
"Thread %d: %s is a link, skipping.\n", * it for the string.
mine->index, */
work->path); search = fopen(work->path, "r");
else if (S_ISDIR (filestat.st_mode)) { if (search == NULL)
DIR *directory; fprintf(stderr,
struct dirent *result; "Unable to open %s: %d (%s)\n",
work->path,
/* errno,
* If the file is a directory, search it and place strerror(errno));
* all files onto the queue as new work items. else {
*/ while (1) {
directory = opendir (work->path); bufptr = fgets(buffer, sizeof(buffer), search);
if (directory == NULL) { if (bufptr == NULL) {
fprintf ( if (feof(search))
stderr, "Unable to open directory %s: %d (%s)\n", break;
work->path, if (ferror(search)) {
errno, strerror (errno)); fprintf(stderr,
continue; "Unable to read %s: %d (%s)\n",
work->path,
errno,
strerror(errno));
break;
} }
}
while (1) { search_ptr = strstr(buffer, work->string);
status = readdir_r (directory, entry, &result); if (search_ptr != NULL) {
if (status != 0) { flockfile(stdout);
fprintf ( printf("Thread %d found \"%s\" in %s\n",
stderr, mine->index,
"Unable to read directory %s: %d (%s)\n", work->string,
work->path, work->path);
status, strerror (status));
break;
}
if (result == NULL)
break; /* End of directory */
/*
* Ignore "." and ".." entries.
*/
if (strcmp (entry->d_name, ".") == 0)
continue;
if (strcmp (entry->d_name, "..") == 0)
continue;
new_work = (work_p)malloc (sizeof (work_t));
if (new_work == NULL)
errno_abort ("Unable to allocate space");
new_work->path = (char*)malloc (path_max);
if (new_work->path == NULL)
errno_abort ("Unable to allocate path");
strcpy (new_work->path, work->path);
strcat (new_work->path, "/");
strcat (new_work->path, entry->d_name);
new_work->string = work->string;
new_work->next = NULL;
status = pthread_mutex_lock (&crew->mutex);
if (status != 0)
err_abort (status, "Lock mutex");
if (crew->first == NULL) {
crew->first = new_work;
crew->last = new_work;
} else {
crew->last->next = new_work;
crew->last = new_work;
}
crew->work_count++;
DPRINTF ((
"Crew %d: add work %#lx, first %#lx, last %#lx, %d\n",
mine->index, new_work, crew->first,
crew->last, crew->work_count));
status = pthread_cond_signal (&crew->go);
status = pthread_mutex_unlock (&crew->mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
}
closedir (directory);
} else if (S_ISREG (filestat.st_mode)) {
FILE *search;
char buffer[256], *bufptr, *search_ptr;
/*
* If this is a file, not a directory, then search
* it for the string.
*/
search = fopen (work->path, "r");
if (search == NULL)
fprintf (
stderr, "Unable to open %s: %d (%s)\n",
work->path,
errno, strerror (errno));
else {
while (1) {
bufptr = fgets (
buffer, sizeof (buffer), search);
if (bufptr == NULL) {
if (feof (search))
break;
if (ferror (search)) {
fprintf (
stderr,
"Unable to read %s: %d (%s)\n",
work->path,
errno, strerror (errno));
break;
}
}
search_ptr = strstr (buffer, work->string);
if (search_ptr != NULL) {
flockfile (stdout);
printf (
"Thread %d found \"%s\" in %s\n",
mine->index, work->string, work->path);
#if 0 #if 0
printf ("%s\n", buffer); printf ("%s\n", buffer);
#endif #endif
funlockfile (stdout); funlockfile(stdout);
break;
}
}
fclose (search);
}
} else
fprintf (
stderr,
"Thread %d: %s is type %o (%s))\n",
mine->index,
work->path,
filestat.st_mode & S_IFMT,
(S_ISFIFO (filestat.st_mode) ? "FIFO"
: (S_ISCHR (filestat.st_mode) ? "CHR"
: (S_ISBLK (filestat.st_mode) ? "BLK"
: (S_ISSOCK (filestat.st_mode) ? "SOCK"
: "unknown")))));
free (work->path); /* Free path buffer */
free (work); /* We're done with this */
/*
* Decrement count of outstanding work items, and wake
* waiters (trying to collect results or start a new
* calculation) if the crew is now idle.
*
* It's important that the count be decremented AFTER
* processing the current work item. That ensures the
* count won't go to 0 until we're really done.
*/
status = pthread_mutex_lock (&crew->mutex);
if (status != 0)
err_abort (status, "Lock crew mutex");
crew->work_count--;
DPRINTF (("Crew %d decremented work to %d\n", mine->index,
crew->work_count));
if (crew->work_count <= 0) {
DPRINTF (("Crew thread %d done\n", mine->index));
status = pthread_cond_broadcast (&crew->done);
if (status != 0)
err_abort (status, "Wake waiters");
status = pthread_mutex_unlock (&crew->mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
break; break;
}
} }
fclose(search);
}
}
else
fprintf(stderr,
"Thread %d: %s is type %o (%s))\n",
mine->index,
work->path,
filestat.st_mode & S_IFMT,
(S_ISFIFO(filestat.st_mode)
? "FIFO"
: (S_ISCHR(filestat.st_mode)
? "CHR"
: (S_ISBLK(filestat.st_mode)
? "BLK"
: (S_ISSOCK(filestat.st_mode) ? "SOCK"
: "unknown")))));
status = pthread_mutex_unlock (&crew->mutex); free(work->path); /* Free path buffer */
if (status != 0) free(work); /* We're done with this */
err_abort (status, "Unlock mutex");
/*
* Decrement count of outstanding work items, and wake
* waiters (trying to collect results or start a new
* calculation) if the crew is now idle.
*
* It's important that the count be decremented AFTER
* processing the current work item. That ensures the
* count won't go to 0 until we're really done.
*/
status = pthread_mutex_lock(&crew->mutex);
if (status != 0)
err_abort(status, "Lock crew mutex");
crew->work_count--;
DPRINTF(
("Crew %d decremented work to %d\n", mine->index, crew->work_count));
if (crew->work_count <= 0) {
DPRINTF(("Crew thread %d done\n", mine->index));
status = pthread_cond_broadcast(&crew->done);
if (status != 0)
err_abort(status, "Wake waiters");
status = pthread_mutex_unlock(&crew->mutex);
if (status != 0)
err_abort(status, "Unlock mutex");
break;
} }
free (entry); status = pthread_mutex_unlock(&crew->mutex);
return NULL; if (status != 0)
err_abort(status, "Unlock mutex");
}
free(entry);
return NULL;
} }
/* /*
* Create a work crew. * Create a work crew.
*/ */
int crew_create (crew_t *crew, int crew_size) int
crew_create(crew_t* crew, int crew_size)
{ {
int crew_index; int crew_index;
int status; int status;
/* /*
* We won't create more than CREW_SIZE members * We won't create more than CREW_SIZE members
*/ */
if (crew_size > CREW_SIZE) if (crew_size > CREW_SIZE)
return EINVAL; return EINVAL;
crew->crew_size = crew_size; crew->crew_size = crew_size;
crew->work_count = 0; crew->work_count = 0;
crew->first = NULL; crew->first = NULL;
crew->last = NULL; crew->last = NULL;
/* /*
* Initialize synchronization objects * Initialize synchronization objects
*/ */
status = pthread_mutex_init (&crew->mutex, NULL); status = pthread_mutex_init(&crew->mutex, NULL);
if (status != 0)
return status;
status = pthread_cond_init(&crew->done, NULL);
if (status != 0)
return status;
status = pthread_cond_init(&crew->go, NULL);
if (status != 0)
return status;
/*
* Create the worker threads.
*/
for (crew_index = 0; crew_index < CREW_SIZE; crew_index++) {
crew->crew[crew_index].index = crew_index;
crew->crew[crew_index].crew = crew;
status = pthread_create(&crew->crew[crew_index].thread,
NULL,
worker_routine,
(void*) &crew->crew[crew_index]);
if (status != 0) if (status != 0)
return status; err_abort(status, "Create worker");
status = pthread_cond_init (&crew->done, NULL); }
if (status != 0) return 0;
return status;
status = pthread_cond_init (&crew->go, NULL);
if (status != 0)
return status;
/*
* Create the worker threads.
*/
for (crew_index = 0; crew_index < CREW_SIZE; crew_index++) {
crew->crew[crew_index].index = crew_index;
crew->crew[crew_index].crew = crew;
status = pthread_create (&crew->crew[crew_index].thread,
NULL, worker_routine, (void*)&crew->crew[crew_index]);
if (status != 0)
err_abort (status, "Create worker");
}
return 0;
} }
/* /*
* Pass a file path to a work crew previously created * Pass a file path to a work crew previously created
* using crew_create * using crew_create
*/ */
int crew_start ( int
crew_p crew, crew_start(crew_p crew, char* filepath, char* search)
char *filepath,
char *search)
{ {
work_p request; work_p request;
int status; int status;
status = pthread_mutex_lock (&crew->mutex); status = pthread_mutex_lock(&crew->mutex);
if (status != 0) if (status != 0)
return status; return status;
/* /*
* If the crew is busy, wait for them to finish. * If the crew is busy, wait for them to finish.
*/ */
while (crew->work_count > 0) { while (crew->work_count > 0) {
status = pthread_cond_wait (&crew->done, &crew->mutex); status = pthread_cond_wait(&crew->done, &crew->mutex);
if (status != 0) {
pthread_mutex_unlock (&crew->mutex);
return status;
}
}
errno = 0;
path_max = pathconf (filepath, _PC_PATH_MAX);
if (path_max == -1) {
if (errno == 0)
path_max = 1024; /* "No limit" */
else
errno_abort ("Unable to get PATH_MAX");
}
errno = 0;
name_max = pathconf (filepath, _PC_NAME_MAX);
if (name_max == -1) {
if (errno == 0)
name_max = 256; /* "No limit" */
else
errno_abort ("Unable to get NAME_MAX");
}
DPRINTF ((
"PATH_MAX for %s is %ld, NAME_MAX is %ld\n",
filepath, path_max, name_max));
path_max++; /* Add null byte */
name_max++; /* Add null byte */
request = (work_p)malloc (sizeof (work_t));
if (request == NULL)
errno_abort ("Unable to allocate request");
DPRINTF (("Requesting %s\n", filepath));
request->path = (char*)malloc (path_max);
if (request->path == NULL)
errno_abort ("Unable to allocate path");
strcpy (request->path, filepath);
request->string = search;
request->next = NULL;
if (crew->first == NULL) {
crew->first = request;
crew->last = request;
} else {
crew->last->next = request;
crew->last = request;
}
crew->work_count++;
status = pthread_cond_signal (&crew->go);
if (status != 0) { if (status != 0) {
free (crew->first); pthread_mutex_unlock(&crew->mutex);
crew->first = NULL; return status;
crew->work_count = 0;
pthread_mutex_unlock (&crew->mutex);
return status;
} }
while (crew->work_count > 0) { }
status = pthread_cond_wait (&crew->done, &crew->mutex);
if (status != 0) errno = 0;
err_abort (status, "waiting for crew to finish"); path_max = pathconf(filepath, _PC_PATH_MAX);
} if (path_max == -1) {
status = pthread_mutex_unlock (&crew->mutex); if (errno == 0)
path_max = 1024; /* "No limit" */
else
errno_abort("Unable to get PATH_MAX");
}
errno = 0;
name_max = pathconf(filepath, _PC_NAME_MAX);
if (name_max == -1) {
if (errno == 0)
name_max = 256; /* "No limit" */
else
errno_abort("Unable to get NAME_MAX");
}
DPRINTF(("PATH_MAX for %s is %ld, NAME_MAX is %ld\n",
filepath,
path_max,
name_max));
path_max++; /* Add null byte */
name_max++; /* Add null byte */
request = (work_p) malloc(sizeof(work_t));
if (request == NULL)
errno_abort("Unable to allocate request");
DPRINTF(("Requesting %s\n", filepath));
request->path = (char*) malloc(path_max);
if (request->path == NULL)
errno_abort("Unable to allocate path");
strcpy(request->path, filepath);
request->string = search;
request->next = NULL;
if (crew->first == NULL) {
crew->first = request;
crew->last = request;
}
else {
crew->last->next = request;
crew->last = request;
}
crew->work_count++;
status = pthread_cond_signal(&crew->go);
if (status != 0) {
free(crew->first);
crew->first = NULL;
crew->work_count = 0;
pthread_mutex_unlock(&crew->mutex);
return status;
}
while (crew->work_count > 0) {
status = pthread_cond_wait(&crew->done, &crew->mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock crew mutex"); err_abort(status, "waiting for crew to finish");
return 0; }
status = pthread_mutex_unlock(&crew->mutex);
if (status != 0)
err_abort(status, "Unlock crew mutex");
return 0;
} }
/* /*
* The main program to "drive" the crew... * The main program to "drive" the crew...
*/ */
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
crew_t my_crew; crew_t my_crew;
char line[128], *next; char line[128], *next;
int status; int status;
if (argc < 3) { if (argc < 3) {
fprintf (stderr, "Usage: %s string path\n", argv[0]); fprintf(stderr, "Usage: %s string path\n", argv[0]);
return -1; return -1;
} }
#ifdef sun #ifdef sun
/* /*
* On Solaris 2.5, threads are not timesliced. To ensure * On Solaris 2.5, threads are not timesliced. To ensure
* that our threads can run concurrently, we need to * that our threads can run concurrently, we need to
* increase the concurrency level to CREW_SIZE. * increase the concurrency level to CREW_SIZE.
*/ */
DPRINTF (("Setting concurrency level to %d\n", CREW_SIZE)); DPRINTF(("Setting concurrency level to %d\n", CREW_SIZE));
thr_setconcurrency (CREW_SIZE); thr_setconcurrency(CREW_SIZE);
#endif #endif
status = crew_create (&my_crew, CREW_SIZE); status = crew_create(&my_crew, CREW_SIZE);
if (status != 0) if (status != 0)
err_abort (status, "Create crew"); err_abort(status, "Create crew");
status = crew_start (&my_crew, argv[2], argv[1]); status = crew_start(&my_crew, argv[2], argv[1]);
if (status != 0) if (status != 0)
err_abort (status, "Start crew"); err_abort(status, "Start crew");
return 0; return 0;
} }

View File

@@ -16,13 +16,13 @@
* stage" where the final thread can stash the value. * stage" where the final thread can stash the value.
*/ */
typedef struct stage_tag { typedef struct stage_tag {
pthread_mutex_t mutex; /* Protect data */ pthread_mutex_t mutex; /* Protect data */
pthread_cond_t avail; /* Data available */ pthread_cond_t avail; /* Data available */
pthread_cond_t ready; /* Ready for data */ pthread_cond_t ready; /* Ready for data */
int data_ready; /* Data present */ int data_ready; /* Data present */
long data; /* Data to process */ long data; /* Data to process */
pthread_t thread; /* Thread for stage */ pthread_t thread; /* Thread for stage */
struct stage_tag *next; /* Next stage */ struct stage_tag* next; /* Next stage */
} stage_t; } stage_t;
/* /*
@@ -30,11 +30,11 @@ typedef struct stage_tag {
* pipeline. * pipeline.
*/ */
typedef struct pipe_tag { typedef struct pipe_tag {
pthread_mutex_t mutex; /* Mutex to protect pipe */ pthread_mutex_t mutex; /* Mutex to protect pipe */
stage_t *head; /* First stage */ stage_t* head; /* First stage */
stage_t *tail; /* Final stage */ stage_t* tail; /* Final stage */
int stages; /* Number of stages */ int stages; /* Number of stages */
int active; /* Active data elements */ int active; /* Active data elements */
} pipe_t; } pipe_t;
/* /*
@@ -42,37 +42,38 @@ typedef struct pipe_tag {
* specified pipe stage. Threads use this to pass * specified pipe stage. Threads use this to pass
* along the modified data item. * along the modified data item.
*/ */
int pipe_send (stage_t *stage, long data) int
pipe_send(stage_t* stage, long data)
{ {
int status; int status;
status = pthread_mutex_lock (&stage->mutex); status = pthread_mutex_lock(&stage->mutex);
if (status != 0) if (status != 0)
return status;
/*
* If there's data in the pipe stage, wait for it
* to be consumed.
*/
while (stage->data_ready) {
status = pthread_cond_wait (&stage->ready, &stage->mutex);
if (status != 0) {
pthread_mutex_unlock (&stage->mutex);
return status;
}
}
/*
* Send the new data
*/
stage->data = data;
stage->data_ready = 1;
status = pthread_cond_signal (&stage->avail);
if (status != 0) {
pthread_mutex_unlock (&stage->mutex);
return status;
}
status = pthread_mutex_unlock (&stage->mutex);
return status; return status;
/*
* If there's data in the pipe stage, wait for it
* to be consumed.
*/
while (stage->data_ready) {
status = pthread_cond_wait(&stage->ready, &stage->mutex);
if (status != 0) {
pthread_mutex_unlock(&stage->mutex);
return status;
}
}
/*
* Send the new data
*/
stage->data = data;
stage->data_ready = 1;
status = pthread_cond_signal(&stage->avail);
if (status != 0) {
pthread_mutex_unlock(&stage->mutex);
return status;
}
status = pthread_mutex_unlock(&stage->mutex);
return status;
} }
/* /*
@@ -81,34 +82,35 @@ int pipe_send (stage_t *stage, long data)
* caller or the previous stage, modify the data * caller or the previous stage, modify the data
* and pass it along to the next (or final) stage. * and pass it along to the next (or final) stage.
*/ */
void *pipe_stage (void *arg) void*
pipe_stage(void* arg)
{ {
stage_t *stage = (stage_t*)arg; stage_t* stage = (stage_t*) arg;
stage_t *next_stage = stage->next; stage_t* next_stage = stage->next;
int status; int status;
status = pthread_mutex_lock (&stage->mutex); status = pthread_mutex_lock(&stage->mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock pipe stage"); err_abort(status, "Lock pipe stage");
while (1) { while (1) {
while (stage->data_ready != 1) { while (stage->data_ready != 1) {
status = pthread_cond_wait (&stage->avail, &stage->mutex); status = pthread_cond_wait(&stage->avail, &stage->mutex);
if (status != 0) if (status != 0)
err_abort (status, "Wait for previous stage"); err_abort(status, "Wait for previous stage");
}
pipe_send (next_stage, stage->data + 1);
stage->data_ready = 0;
status = pthread_cond_signal (&stage->ready);
if (status != 0)
err_abort (status, "Wake next stage");
} }
/* pipe_send(next_stage, stage->data + 1);
* Notice that the routine never unlocks the stage->mutex. stage->data_ready = 0;
* The call to pthread_cond_wait implicitly unlocks the status = pthread_cond_signal(&stage->ready);
* mutex while the thread is waiting, allowing other threads if (status != 0)
* to make progress. Because the loop never terminates, this err_abort(status, "Wake next stage");
* function has no need to unlock the mutex explicitly. }
*/ /*
* Notice that the routine never unlocks the stage->mutex.
* The call to pthread_cond_wait implicitly unlocks the
* mutex while the thread is waiting, allowing other threads
* to make progress. Because the loop never terminates, this
* function has no need to unlock the mutex explicitly.
*/
} }
/* /*
@@ -116,61 +118,59 @@ void *pipe_stage (void *arg)
* data is initialized and the threads created. They'll * data is initialized and the threads created. They'll
* wait for data. * wait for data.
*/ */
int pipe_create (pipe_t *pipe, int stages) int
pipe_create(pipe_t* pipe, int stages)
{ {
int pipe_index; int pipe_index;
stage_t **link = &pipe->head, *new_stage, *stage; stage_t **link = &pipe->head, *new_stage, *stage;
int status; int status;
status = pthread_mutex_init (&pipe->mutex, NULL); status = pthread_mutex_init(&pipe->mutex, NULL);
if (status != 0)
err_abort(status, "Init pipe mutex");
pipe->stages = stages;
pipe->active = 0;
for (pipe_index = 0; pipe_index <= stages; pipe_index++) {
new_stage = (stage_t*) malloc(sizeof(stage_t));
if (new_stage == NULL)
errno_abort("Allocate stage");
status = pthread_mutex_init(&new_stage->mutex, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Init pipe mutex"); err_abort(status, "Init stage mutex");
pipe->stages = stages; status = pthread_cond_init(&new_stage->avail, NULL);
pipe->active = 0; if (status != 0)
err_abort(status, "Init avail condition");
status = pthread_cond_init(&new_stage->ready, NULL);
if (status != 0)
err_abort(status, "Init ready condition");
new_stage->data_ready = 0;
*link = new_stage;
link = &new_stage->next;
}
for (pipe_index = 0; pipe_index <= stages; pipe_index++) { *link = (stage_t*) NULL; /* Terminate list */
new_stage = (stage_t*)malloc (sizeof (stage_t)); pipe->tail = new_stage; /* Record the tail */
if (new_stage == NULL)
errno_abort ("Allocate stage");
status = pthread_mutex_init (&new_stage->mutex, NULL);
if (status != 0)
err_abort (status, "Init stage mutex");
status = pthread_cond_init (&new_stage->avail, NULL);
if (status != 0)
err_abort (status, "Init avail condition");
status = pthread_cond_init (&new_stage->ready, NULL);
if (status != 0)
err_abort (status, "Init ready condition");
new_stage->data_ready = 0;
*link = new_stage;
link = &new_stage->next;
}
*link = (stage_t*)NULL; /* Terminate list */ /*
pipe->tail = new_stage; /* Record the tail */ * Create the threads for the pipe stages only after all
* the data is initialized (including all links). Note
/* * that the last stage doesn't get a thread, it's just
* Create the threads for the pipe stages only after all * a receptacle for the final pipeline value.
* the data is initialized (including all links). Note *
* that the last stage doesn't get a thread, it's just * At this point, proper cleanup on an error would take up
* a receptacle for the final pipeline value. * more space than worthwhile in a "simple example", so
* * instead of cancelling and detaching all the threads
* At this point, proper cleanup on an error would take up * already created, plus the synchronization object and
* more space than worthwhile in a "simple example", so * memory cleanup done for earlier errors, it will simply
* instead of cancelling and detaching all the threads * abort.
* already created, plus the synchronization object and */
* memory cleanup done for earlier errors, it will simply for (stage = pipe->head; stage->next != NULL; stage = stage->next) {
* abort. status = pthread_create(&stage->thread, NULL, pipe_stage, (void*) stage);
*/ if (status != 0)
for ( stage = pipe->head; err_abort(status, "Create pipe stage");
stage->next != NULL; }
stage = stage->next) { return 0;
status = pthread_create (
&stage->thread, NULL, pipe_stage, (void*)stage);
if (status != 0)
err_abort (status, "Create pipe stage");
}
return 0;
} }
/* /*
@@ -181,83 +181,88 @@ int pipe_create (pipe_t *pipe, int stages)
* (note that the pipe will stall when each stage fills, * (note that the pipe will stall when each stage fills,
* until the result is collected). * until the result is collected).
*/ */
int pipe_start (pipe_t *pipe, long value) int
pipe_start(pipe_t* pipe, long value)
{ {
int status; int status;
status = pthread_mutex_lock (&pipe->mutex); status = pthread_mutex_lock(&pipe->mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock pipe mutex"); err_abort(status, "Lock pipe mutex");
pipe->active++; pipe->active++;
status = pthread_mutex_unlock (&pipe->mutex); status = pthread_mutex_unlock(&pipe->mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock pipe mutex"); err_abort(status, "Unlock pipe mutex");
pipe_send (pipe->head, value); pipe_send(pipe->head, value);
return 0; return 0;
} }
/* /*
* Collect the result of the pipeline. Wait for a * Collect the result of the pipeline. Wait for a
* result if the pipeline hasn't produced one. * result if the pipeline hasn't produced one.
*/ */
int pipe_result (pipe_t *pipe, long *result) int
pipe_result(pipe_t* pipe, long* result)
{ {
stage_t *tail = pipe->tail; stage_t* tail = pipe->tail;
long value; long value;
int empty = 0; int empty = 0;
int status; int status;
status = pthread_mutex_lock (&pipe->mutex); status = pthread_mutex_lock(&pipe->mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock pipe mutex"); err_abort(status, "Lock pipe mutex");
if (pipe->active <= 0) if (pipe->active <= 0)
empty = 1; empty = 1;
else else
pipe->active--; pipe->active--;
status = pthread_mutex_unlock (&pipe->mutex); status = pthread_mutex_unlock(&pipe->mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock pipe mutex"); err_abort(status, "Unlock pipe mutex");
if (empty) if (empty)
return 0; return 0;
pthread_mutex_lock (&tail->mutex); pthread_mutex_lock(&tail->mutex);
while (!tail->data_ready) while (!tail->data_ready) pthread_cond_wait(&tail->avail, &tail->mutex);
pthread_cond_wait (&tail->avail, &tail->mutex); *result = tail->data;
*result = tail->data; tail->data_ready = 0;
tail->data_ready = 0; pthread_cond_signal(&tail->ready);
pthread_cond_signal (&tail->ready); pthread_mutex_unlock(&tail->mutex);
pthread_mutex_unlock (&tail->mutex); return 1;
return 1;
} }
/* /*
* The main program to "drive" the pipeline... * The main program to "drive" the pipeline...
*/ */
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pipe_t my_pipe; pipe_t my_pipe;
long value, result; long value, result;
int status; int status;
char line[128]; char line[128];
pipe_create (&my_pipe, 10); pipe_create(&my_pipe, 10);
printf ("Enter integer values, or \"=\" for next result\n"); printf("Enter integer values, or \"=\" for next result\n");
while (1) { while (1) {
printf ("Data> "); printf("Data> ");
if (fgets (line, sizeof (line), stdin) == NULL) exit (0); if (fgets(line, sizeof(line), stdin) == NULL)
if (strlen (line) <= 1) continue; exit(0);
if (strlen (line) <= 2 && line[0] == '=') { if (strlen(line) <= 1)
if (pipe_result (&my_pipe, &result)) continue;
printf ("Result is %ld\n", result); if (strlen(line) <= 2 && line[0] == '=') {
else if (pipe_result(&my_pipe, &result))
printf ("Pipe is empty\n"); printf("Result is %ld\n", result);
} else { else
if (sscanf (line, "%ld", &value) < 1) printf("Pipe is empty\n");
fprintf (stderr, "Enter an integer value\n");
else
pipe_start (&my_pipe, value);
}
} }
else {
if (sscanf(line, "%ld", &value) < 1)
fprintf(stderr, "Enter an integer value\n");
else
pipe_start(&my_pipe, value);
}
}
} }

View File

@@ -26,7 +26,7 @@ struct stage_tag {
bool isDataAvail; /* Data present */ bool isDataAvail; /* Data present */
long data; /* Data to process */ long data; /* Data to process */
pthread_t thread; /* Thread for stage */ pthread_t thread; /* Thread for stage */
stage_tag *next; stage_tag* next;
}; };
using stage_t = stage_tag; using stage_t = stage_tag;
@@ -50,11 +50,14 @@ using pipe_t = pipe_tag;
* specified pipe stage. Threads use this to pass * specified pipe stage. Threads use this to pass
* along the modified data item. * along the modified data item.
*/ */
int pipe_send(stage_t &stage, long data) { int
pipe_send(stage_t& stage, long data)
{
int status; int status;
status = pthread_mutex_lock(&stage.mutex); status = pthread_mutex_lock(&stage.mutex);
if (status != 0) return status; if (status != 0)
return status;
/* /*
* If there's data in the pipe stage, wait for it * If there's data in the pipe stage, wait for it
@@ -71,7 +74,7 @@ int pipe_send(stage_t &stage, long data) {
/* /*
* Send the new data * Send the new data
*/ */
stage.data = data; stage.data = data;
stage.isDataAvail = true; stage.isDataAvail = true;
status = pthread_cond_signal(&stage.dataIsAvail); status = pthread_cond_signal(&stage.dataIsAvail);
@@ -90,18 +93,22 @@ int pipe_send(stage_t &stage, long data) {
* caller or the previous stage, modify the data * caller or the previous stage, modify the data
* and pass it along to the next (or final) stage. * and pass it along to the next (or final) stage.
*/ */
void *pipe_stage(void *arg) { void*
stage_t &stage = *(stage_t *)arg; pipe_stage(void* arg)
stage_t &nextStage = *stage.next; {
stage_t& stage = *(stage_t*) arg;
stage_t& nextStage = *stage.next;
int status; int status;
status = pthread_mutex_lock(&stage.mutex); status = pthread_mutex_lock(&stage.mutex);
if (status != 0) err_abort(status, "Lock pipe stage"); if (status != 0)
err_abort(status, "Lock pipe stage");
while (1) { while (1) {
while (!stage.isDataAvail) { while (!stage.isDataAvail) {
status = pthread_cond_wait(&stage.dataIsAvail, &stage.mutex); status = pthread_cond_wait(&stage.dataIsAvail, &stage.mutex);
if (status != 0) err_abort(status, "Wait for previous stage"); if (status != 0)
err_abort(status, "Wait for previous stage");
} }
pipe_send(nextStage, stage.data + 1); pipe_send(nextStage, stage.data + 1);
@@ -109,7 +116,8 @@ void *pipe_stage(void *arg) {
stage.isDataAvail = false; stage.isDataAvail = false;
status = pthread_cond_signal(&stage.threadIsIdle); status = pthread_cond_signal(&stage.threadIsIdle);
if (status != 0) err_abort(status, "Wake next stage"); if (status != 0)
err_abort(status, "Wake next stage");
} }
/* /*
@@ -126,20 +134,26 @@ void *pipe_stage(void *arg) {
* data is initialized and the threads created. They'll * data is initialized and the threads created. They'll
* wait for data. * wait for data.
*/ */
int pipe_create(pipe_t &pipe, size_t nStages) { int
pipe_create(pipe_t& pipe, size_t nStages)
{
int status = pthread_mutex_init(&pipe.mutex, NULL); int status = pthread_mutex_init(&pipe.mutex, NULL);
if (status != 0) err_abort(status, "Init pipe mutex"); if (status != 0)
err_abort(status, "Init pipe mutex");
pipe.stageList.resize(nStages); pipe.stageList.resize(nStages);
pipe.nActive = 0; pipe.nActive = 0;
for (auto &iStage : pipe.stageList) { for (auto& iStage : pipe.stageList) {
status = pthread_mutex_init(&iStage.mutex, NULL); status = pthread_mutex_init(&iStage.mutex, NULL);
if (status != 0) err_abort(status, "Init stage mutex"); if (status != 0)
err_abort(status, "Init stage mutex");
status = pthread_cond_init(&iStage.dataIsAvail, NULL); status = pthread_cond_init(&iStage.dataIsAvail, NULL);
if (status != 0) err_abort(status, "Init dataIsAvail condition"); if (status != 0)
err_abort(status, "Init dataIsAvail condition");
status = pthread_cond_init(&iStage.threadIsIdle, NULL); status = pthread_cond_init(&iStage.threadIsIdle, NULL);
if (status != 0) err_abort(status, "Init threadIsIdle condition"); if (status != 0)
err_abort(status, "Init threadIsIdle condition");
iStage.isDataAvail = false; iStage.isDataAvail = false;
} }
@@ -158,11 +172,12 @@ int pipe_create(pipe_t &pipe, size_t nStages) {
*/ */
for (auto it = pipe.stageList.begin(), ite = --pipe.stageList.end(); for (auto it = pipe.stageList.begin(), ite = --pipe.stageList.end();
it != ite;) { it != ite;) {
auto iit = it++; auto iit = it++;
iit->next = &(*it); iit->next = &(*it);
status = pthread_create(&iit->thread, NULL, pipe_stage, (void *)&*iit); status = pthread_create(&iit->thread, NULL, pipe_stage, (void*) &*iit);
if (status != 0) err_abort(status, "Create pipe stage"); if (status != 0)
err_abort(status, "Create pipe stage");
} }
return 0; return 0;
} }
@@ -171,11 +186,14 @@ int pipe_create(pipe_t &pipe, size_t nStages) {
* Collect the result of the pipeline. Wait for a * Collect the result of the pipeline. Wait for a
* result if the pipeline hasn't produced one. * result if the pipeline hasn't produced one.
*/ */
int pipe_result(pipe_t &pipe) { int
pipe_result(pipe_t& pipe)
{
int status; int status;
status = pthread_mutex_lock(&pipe.mutex); status = pthread_mutex_lock(&pipe.mutex);
if (status != 0) err_abort(status, "Lock pipe mutex"); if (status != 0)
err_abort(status, "Lock pipe mutex");
bool isEmpty = false; bool isEmpty = false;
if (pipe.nActive <= 0) if (pipe.nActive <= 0)
@@ -184,30 +202,34 @@ int pipe_result(pipe_t &pipe) {
pipe.nActive--; pipe.nActive--;
status = pthread_mutex_unlock(&pipe.mutex); status = pthread_mutex_unlock(&pipe.mutex);
if (status != 0) err_abort(status, "Unlock pipe mutex"); if (status != 0)
err_abort(status, "Unlock pipe mutex");
if (isEmpty) { if (isEmpty) {
printf("Pipe is empty\n"); printf("Pipe is empty\n");
return 0; return 0;
} }
auto &tail = pipe.stageList.back(); auto& tail = pipe.stageList.back();
status = pthread_mutex_lock(&tail.mutex); status = pthread_mutex_lock(&tail.mutex);
if (status != 0) err_abort(status, "Lock pipe tail mutex"); if (status != 0)
err_abort(status, "Lock pipe tail mutex");
while (!tail.isDataAvail) { while (!tail.isDataAvail) {
pthread_cond_wait(&tail.dataIsAvail, &tail.mutex); pthread_cond_wait(&tail.dataIsAvail, &tail.mutex);
} }
long result = tail.data; long result = tail.data;
tail.isDataAvail = false; tail.isDataAvail = false;
status = pthread_cond_signal(&tail.threadIsIdle); status = pthread_cond_signal(&tail.threadIsIdle);
if (status != 0) err_abort(status, "Signal pipe tail threadIsIdle"); if (status != 0)
err_abort(status, "Signal pipe tail threadIsIdle");
status = pthread_mutex_unlock(&tail.mutex); status = pthread_mutex_unlock(&tail.mutex);
if (status != 0) err_abort(status, "Unlock pipe tail mutex"); if (status != 0)
err_abort(status, "Unlock pipe tail mutex");
printf("Result is %ld\n", result); printf("Result is %ld\n", result);
@@ -222,9 +244,12 @@ int pipe_result(pipe_t &pipe) {
* (note that the pipe will stall when each stage fills, * (note that the pipe will stall when each stage fills,
* until the result is collected). * until the result is collected).
*/ */
void pipe_start(pipe_t &pipe, long value) { void
pipe_start(pipe_t& pipe, long value)
{
int status = pthread_mutex_lock(&pipe.mutex); int status = pthread_mutex_lock(&pipe.mutex);
if (status != 0) err_abort(status, "Lock pipe mutex"); if (status != 0)
err_abort(status, "Lock pipe mutex");
if (pipe.nActive > pipe.stageList.size() - 1) { if (pipe.nActive > pipe.stageList.size() - 1) {
status = pthread_mutex_unlock(&pipe.mutex); status = pthread_mutex_unlock(&pipe.mutex);
@@ -235,7 +260,8 @@ void pipe_start(pipe_t &pipe, long value) {
pipe.nActive++; pipe.nActive++;
status = pthread_mutex_unlock(&pipe.mutex); status = pthread_mutex_unlock(&pipe.mutex);
if (status != 0) err_abort(status, "Unlock pipe mutex"); if (status != 0)
err_abort(status, "Unlock pipe mutex");
pipe_send(pipe.stageList.front(), value); pipe_send(pipe.stageList.front(), value);
} }
@@ -243,7 +269,9 @@ void pipe_start(pipe_t &pipe, long value) {
/* /*
* The main program to "drive" the pipeline... * The main program to "drive" the pipeline...
*/ */
int main(int argc, char *argv[]) { int
main(int argc, char* argv[])
{
pipe_t my_pipe; pipe_t my_pipe;
pipe_create(my_pipe, 2); pipe_create(my_pipe, 2);
@@ -252,12 +280,15 @@ int main(int argc, char *argv[]) {
char line[128]; char line[128];
while (1) { while (1) {
printf("Data> "); printf("Data> ");
if (fgets(line, sizeof(line), stdin) == NULL) exit(0); if (fgets(line, sizeof(line), stdin) == NULL)
exit(0);
printf(line); printf(line);
if (strlen(line) <= 1) continue; if (strlen(line) <= 1)
continue;
if (strlen(line) <= 2 && line[0] == '=') { if (strlen(line) <= 2 && line[0] == '=') {
pipe_result(my_pipe); pipe_result(my_pipe);
} else { }
else {
long value; long value;
if (sscanf(line, "%ld", &value) < 1) if (sscanf(line, "%ld", &value) < 1)
fprintf(stderr, "Enter an integer value\n"); fprintf(stderr, "Enter an integer value\n");

View File

@@ -7,43 +7,42 @@
* to allow interleaved thread execution, since threads are not * to allow interleaved thread execution, since threads are not
* timesliced. * timesliced.
*/ */
#include <pthread.h>
#include <math.h> #include <math.h>
#include <pthread.h>
#include "errors.h" #include "errors.h"
#define CLIENT_THREADS 4 /* Number of clients */ #define CLIENT_THREADS 4 /* Number of clients */
#define REQ_READ 1 /* Read with prompt */ #define REQ_READ 1 /* Read with prompt */
#define REQ_WRITE 2 /* Write */ #define REQ_WRITE 2 /* Write */
#define REQ_QUIT 3 /* Quit server */ #define REQ_QUIT 3 /* Quit server */
/* /*
* Internal to server "package" -- one for each request. * Internal to server "package" -- one for each request.
*/ */
typedef struct request_tag { typedef struct request_tag {
struct request_tag *next; /* Link to next */ struct request_tag* next; /* Link to next */
int operation; /* Function code */ int operation; /* Function code */
int synchronous; /* Non-zero if synchronous */ int synchronous; /* Non-zero if synchronous */
int done_flag; /* Predicate for wait */ int done_flag; /* Predicate for wait */
pthread_cond_t done; /* Wait for completion */ pthread_cond_t done; /* Wait for completion */
char prompt[32]; /* Prompt string for reads */ char prompt[32]; /* Prompt string for reads */
char text[128]; /* Read/write text */ char text[128]; /* Read/write text */
} request_t; } request_t;
/* /*
* Static context for the server * Static context for the server
*/ */
typedef struct tty_server_tag { typedef struct tty_server_tag {
request_t *first; request_t* first;
request_t *last; request_t* last;
int running; int running;
pthread_mutex_t mutex; pthread_mutex_t mutex;
pthread_cond_t request; pthread_cond_t request;
} tty_server_t; } tty_server_t;
tty_server_t tty_server = { tty_server_t tty_server = {
NULL, NULL, 0, NULL, NULL, 0, PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};
PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};
/* /*
* Main program data * Main program data
@@ -51,7 +50,7 @@ tty_server_t tty_server = {
int client_threads; int client_threads;
pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t clients_done = PTHREAD_COND_INITIALIZER; pthread_cond_t clients_done = PTHREAD_COND_INITIALIZER;
/* /*
* The server start routine. It waits for a request to appear * The server start routine. It waits for a request to appear
@@ -62,267 +61,264 @@ pthread_cond_t clients_done = PTHREAD_COND_INITIALIZER;
* responsible for freeing the request. If the request was not * responsible for freeing the request. If the request was not
* synchronous, the server will free the request on completion. * synchronous, the server will free the request on completion.
*/ */
void *tty_server_routine (void *arg) void*
tty_server_routine(void* arg)
{ {
static pthread_mutex_t prompt_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t prompt_mutex = PTHREAD_MUTEX_INITIALIZER;
request_t *request; request_t* request;
int operation, len; int operation, len;
int status; int status;
while (1) { while (1) {
status = pthread_mutex_lock (&tty_server.mutex); status = pthread_mutex_lock(&tty_server.mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock server mutex"); err_abort(status, "Lock server mutex");
/* /*
* Wait for data * Wait for data
*/ */
while (tty_server.first == NULL) { while (tty_server.first == NULL) {
status = pthread_cond_wait ( status = pthread_cond_wait(&tty_server.request, &tty_server.mutex);
&tty_server.request, &tty_server.mutex); if (status != 0)
if (status != 0) err_abort(status, "Wait for request");
err_abort (status, "Wait for request");
}
request = tty_server.first;
tty_server.first = request->next;
if (tty_server.first == NULL)
tty_server.last = NULL;
status = pthread_mutex_unlock (&tty_server.mutex);
if (status != 0)
err_abort (status, "Unlock server mutex");
/*
* Process the data
*/
operation = request->operation;
switch (operation) {
case REQ_QUIT:
break;
case REQ_READ:
if (strlen (request->prompt) > 0)
printf (request->prompt);
if (fgets (request->text, 128, stdin) == NULL)
request->text[0] = '\0';
/*
* Because fgets returns the newline, and we don't want it,
* we look for it, and turn it into a null (truncating the
* input) if found. It should be the last character, if it is
* there.
*/
len = strlen (request->text);
if (len > 0 && request->text[len-1] == '\n')
request->text[len-1] = '\0';
break;
case REQ_WRITE:
puts (request->text);
break;
default:
break;
}
if (request->synchronous) {
status = pthread_mutex_lock (&tty_server.mutex);
if (status != 0)
err_abort (status, "Lock server mutex");
request->done_flag = 1;
status = pthread_cond_signal (&request->done);
if (status != 0)
err_abort (status, "Signal server condition");
status = pthread_mutex_unlock (&tty_server.mutex);
if (status != 0)
err_abort (status, "Unlock server mutex");
} else
free (request);
if (operation == REQ_QUIT)
break;
} }
return NULL; request = tty_server.first;
tty_server.first = request->next;
if (tty_server.first == NULL)
tty_server.last = NULL;
status = pthread_mutex_unlock(&tty_server.mutex);
if (status != 0)
err_abort(status, "Unlock server mutex");
/*
* Process the data
*/
operation = request->operation;
switch (operation) {
case REQ_QUIT:
break;
case REQ_READ:
if (strlen(request->prompt) > 0)
printf(request->prompt);
if (fgets(request->text, 128, stdin) == NULL)
request->text[0] = '\0';
/*
* Because fgets returns the newline, and we don't want it,
* we look for it, and turn it into a null (truncating the
* input) if found. It should be the last character, if it is
* there.
*/
len = strlen(request->text);
if (len > 0 && request->text[len - 1] == '\n')
request->text[len - 1] = '\0';
break;
case REQ_WRITE:
puts(request->text);
break;
default:
break;
}
if (request->synchronous) {
status = pthread_mutex_lock(&tty_server.mutex);
if (status != 0)
err_abort(status, "Lock server mutex");
request->done_flag = 1;
status = pthread_cond_signal(&request->done);
if (status != 0)
err_abort(status, "Signal server condition");
status = pthread_mutex_unlock(&tty_server.mutex);
if (status != 0)
err_abort(status, "Unlock server mutex");
}
else
free(request);
if (operation == REQ_QUIT)
break;
}
return NULL;
} }
/* /*
* Request an operation * Request an operation
*/ */
void tty_server_request ( void
int operation, tty_server_request(int operation, int sync, const char* prompt, char* string)
int sync,
const char *prompt,
char *string)
{ {
request_t *request; request_t* request;
int status; int status;
status = pthread_mutex_lock (&tty_server.mutex); status = pthread_mutex_lock(&tty_server.mutex);
if (status != 0)
err_abort(status, "Lock server mutex");
if (!tty_server.running) {
pthread_t thread;
pthread_attr_t detached_attr;
status = pthread_attr_init(&detached_attr);
if (status != 0) if (status != 0)
err_abort (status, "Lock server mutex"); err_abort(status, "Init attributes object");
if (!tty_server.running) { status =
pthread_t thread; pthread_attr_setdetachstate(&detached_attr, PTHREAD_CREATE_DETACHED);
pthread_attr_t detached_attr;
status = pthread_attr_init (&detached_attr);
if (status != 0)
err_abort (status, "Init attributes object");
status = pthread_attr_setdetachstate (
&detached_attr, PTHREAD_CREATE_DETACHED);
if (status != 0)
err_abort (status, "Set detach state");
tty_server.running = 1;
status = pthread_create (&thread, &detached_attr,
tty_server_routine, NULL);
if (status != 0)
err_abort (status, "Create server");
/*
* Ignore an error in destroying the attributes object.
* It's unlikely to fail, there's nothing useful we can
* do about it, and it's not worth aborting the program
* over it.
*/
pthread_attr_destroy (&detached_attr);
}
/*
* Create and initialize a request structure.
*/
request = (request_t*)malloc (sizeof (request_t));
if (request == NULL)
errno_abort ("Allocate request");
request->next = NULL;
request->operation = operation;
request->synchronous = sync;
if (sync) {
request->done_flag = 0;
status = pthread_cond_init (&request->done, NULL);
if (status != 0)
err_abort (status, "Init request condition");
}
if (prompt != NULL)
strncpy (request->prompt, prompt, 32);
else
request->prompt[0] = '\0';
if (operation == REQ_WRITE && string != NULL)
strncpy (request->text, string, 128);
else
request->text[0] = '\0';
/*
* Add the request to the queue, maintaining the first and
* last pointers.
*/
if (tty_server.first == NULL) {
tty_server.first = request;
tty_server.last = request;
} else {
(tty_server.last)->next = request;
tty_server.last = request;
}
/*
* Tell the server that a request is available.
*/
status = pthread_cond_signal (&tty_server.request);
if (status != 0) if (status != 0)
err_abort (status, "Wake server"); err_abort(status, "Set detach state");
tty_server.running = 1;
status = pthread_create(&thread, &detached_attr, tty_server_routine, NULL);
if (status != 0)
err_abort(status, "Create server");
/* /*
* If the request was "synchronous", then wait for a reply. * Ignore an error in destroying the attributes object.
* It's unlikely to fail, there's nothing useful we can
* do about it, and it's not worth aborting the program
* over it.
*/ */
if (sync) { pthread_attr_destroy(&detached_attr);
while (!request->done_flag) { }
status = pthread_cond_wait (
&request->done, &tty_server.mutex); /*
if (status != 0) * Create and initialize a request structure.
err_abort (status, "Wait for sync request"); */
} request = (request_t*) malloc(sizeof(request_t));
if (operation == REQ_READ) { if (request == NULL)
if (strlen (request->text) > 0) errno_abort("Allocate request");
strcpy (string, request->text); request->next = NULL;
else request->operation = operation;
string[0] = '\0'; request->synchronous = sync;
} if (sync) {
status = pthread_cond_destroy (&request->done); request->done_flag = 0;
if (status != 0) status = pthread_cond_init(&request->done, NULL);
err_abort (status, "Destroy request condition");
free (request);
}
status = pthread_mutex_unlock (&tty_server.mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock mutex"); err_abort(status, "Init request condition");
}
if (prompt != NULL)
strncpy(request->prompt, prompt, 32);
else
request->prompt[0] = '\0';
if (operation == REQ_WRITE && string != NULL)
strncpy(request->text, string, 128);
else
request->text[0] = '\0';
/*
* Add the request to the queue, maintaining the first and
* last pointers.
*/
if (tty_server.first == NULL) {
tty_server.first = request;
tty_server.last = request;
}
else {
(tty_server.last)->next = request;
tty_server.last = request;
}
/*
* Tell the server that a request is available.
*/
status = pthread_cond_signal(&tty_server.request);
if (status != 0)
err_abort(status, "Wake server");
/*
* If the request was "synchronous", then wait for a reply.
*/
if (sync) {
while (!request->done_flag) {
status = pthread_cond_wait(&request->done, &tty_server.mutex);
if (status != 0)
err_abort(status, "Wait for sync request");
}
if (operation == REQ_READ) {
if (strlen(request->text) > 0)
strcpy(string, request->text);
else
string[0] = '\0';
}
status = pthread_cond_destroy(&request->done);
if (status != 0)
err_abort(status, "Destroy request condition");
free(request);
}
status = pthread_mutex_unlock(&tty_server.mutex);
if (status != 0)
err_abort(status, "Unlock mutex");
} }
/* /*
* Client routine -- multiple copies will request server. * Client routine -- multiple copies will request server.
*/ */
void *client_routine (void *arg) void*
client_routine(void* arg)
{ {
int my_number = (int)arg, loops; int my_number = (int) arg, loops;
char prompt[32]; char prompt[32];
char string[128], formatted[128]; char string[128], formatted[128];
int status; int status;
sprintf (prompt, "Client %d> ", my_number); sprintf(prompt, "Client %d> ", my_number);
while (1) { while (1) {
tty_server_request (REQ_READ, 1, prompt, string); tty_server_request(REQ_READ, 1, prompt, string);
if (strlen (string) == 0) if (strlen(string) == 0)
break; break;
for (loops = 0; loops < 4; loops++) { for (loops = 0; loops < 4; loops++) {
sprintf ( sprintf(formatted, "(%d#%d) %s", my_number, loops, string);
formatted, "(%d#%d) %s", my_number, loops, string); tty_server_request(REQ_WRITE, 0, NULL, formatted);
tty_server_request (REQ_WRITE, 0, NULL, formatted); sleep(1);
sleep (1);
}
} }
status = pthread_mutex_lock (&client_mutex); }
status = pthread_mutex_lock(&client_mutex);
if (status != 0)
err_abort(status, "Lock client mutex");
client_threads--;
if (client_threads <= 0) {
status = pthread_cond_signal(&clients_done);
if (status != 0) if (status != 0)
err_abort (status, "Lock client mutex"); err_abort(status, "Signal clients done");
client_threads--; }
if (client_threads <= 0) { status = pthread_mutex_unlock(&client_mutex);
status = pthread_cond_signal (&clients_done); if (status != 0)
if (status != 0) err_abort(status, "Unlock client mutex");
err_abort (status, "Signal clients done"); return NULL;
}
status = pthread_mutex_unlock (&client_mutex);
if (status != 0)
err_abort (status, "Unlock client mutex");
return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t thread; pthread_t thread;
int count; int count;
int status; int status;
#ifdef sun #ifdef sun
/* /*
* On Solaris 2.5, threads are not timesliced. To ensure * On Solaris 2.5, threads are not timesliced. To ensure
* that our threads can run concurrently, we need to * that our threads can run concurrently, we need to
* increase the concurrency level to CLIENT_THREADS. * increase the concurrency level to CLIENT_THREADS.
*/ */
DPRINTF (("Setting concurrency level to %d\n", CLIENT_THREADS)); DPRINTF(("Setting concurrency level to %d\n", CLIENT_THREADS));
thr_setconcurrency (CLIENT_THREADS); thr_setconcurrency(CLIENT_THREADS);
#endif #endif
/* /*
* Create CLIENT_THREADS clients. * Create CLIENT_THREADS clients.
*/ */
client_threads = CLIENT_THREADS; client_threads = CLIENT_THREADS;
for (count = 0; count < client_threads; count++) { for (count = 0; count < client_threads; count++) {
status = pthread_create (&thread, NULL, status = pthread_create(&thread, NULL, client_routine, (void*) count);
client_routine, (void*)count);
if (status != 0)
err_abort (status, "Create client thread");
}
status = pthread_mutex_lock (&client_mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock client mutex"); err_abort(status, "Create client thread");
while (client_threads > 0) { }
status = pthread_cond_wait (&clients_done, &client_mutex); status = pthread_mutex_lock(&client_mutex);
if (status != 0) if (status != 0)
err_abort (status, "Wait for clients to finish"); err_abort(status, "Lock client mutex");
} while (client_threads > 0) {
status = pthread_mutex_unlock (&client_mutex); status = pthread_cond_wait(&clients_done, &client_mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock client mutex"); err_abort(status, "Wait for clients to finish");
printf ("All clients done\n"); }
tty_server_request (REQ_QUIT, 1, NULL, NULL); status = pthread_mutex_unlock(&client_mutex);
return 0; if (status != 0)
err_abort(status, "Unlock client mutex");
printf("All clients done\n");
tty_server_request(REQ_QUIT, 1, NULL, NULL);
return 0;
} }

View File

@@ -18,49 +18,50 @@ static int counter;
* when it calls pthread_testcancel, which it does each 1000 * when it calls pthread_testcancel, which it does each 1000
* iterations. * iterations.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
DPRINTF (("thread_routine starting\n")); DPRINTF(("thread_routine starting\n"));
for (counter = 0; ; counter++) for (counter = 0;; counter++)
if ((counter % 1000) == 0) { if ((counter % 1000) == 0) {
DPRINTF (("calling testcancel\n")); DPRINTF(("calling testcancel\n"));
pthread_testcancel (); pthread_testcancel();
} }
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t thread_id; pthread_t thread_id;
void *result; void* result;
int status; int status;
#ifdef sun #ifdef sun
/* /*
* On Solaris 2.5, threads are not timesliced. To ensure * On Solaris 2.5, threads are not timesliced. To ensure
* that our two threads can run concurrently, we need to * that our two threads can run concurrently, we need to
* increase the concurrency level to 2. * increase the concurrency level to 2.
*/ */
DPRINTF (("Setting concurrency level to 2\n")); DPRINTF(("Setting concurrency level to 2\n"));
thr_setconcurrency (2); thr_setconcurrency(2);
#endif #endif
status = pthread_create ( status = pthread_create(&thread_id, NULL, thread_routine, NULL);
&thread_id, NULL, thread_routine, NULL); if (status != 0)
if (status != 0) err_abort(status, "Create thread");
err_abort (status, "Create thread"); sleep(2);
sleep (2);
DPRINTF (("calling cancel\n")); DPRINTF(("calling cancel\n"));
status = pthread_cancel (thread_id); status = pthread_cancel(thread_id);
if (status != 0) if (status != 0)
err_abort (status, "Cancel thread"); err_abort(status, "Cancel thread");
DPRINTF (("calling join\n")); DPRINTF(("calling join\n"));
status = pthread_join (thread_id, &result); status = pthread_join(thread_id, &result);
if (status != 0) if (status != 0)
err_abort (status, "Join thread"); err_abort(status, "Join thread");
if (result == PTHREAD_CANCELED) if (result == PTHREAD_CANCELED)
printf ("Thread cancelled at iteration %d\n", counter); printf("Thread cancelled at iteration %d\n", counter);
else else
printf ("Thread was not cancelled\n"); printf("Thread was not cancelled\n");
return 0; return 0;
} }

View File

@@ -11,30 +11,30 @@
#include <pthread.h> #include <pthread.h>
#include "errors.h" #include "errors.h"
#define SIZE 10 /* array size */ #define SIZE 10 /* array size */
static int matrixa[SIZE][SIZE]; static int matrixa[SIZE][SIZE];
static int matrixb[SIZE][SIZE]; static int matrixb[SIZE][SIZE];
static int matrixc[SIZE][SIZE]; static int matrixc[SIZE][SIZE];
#ifdef DEBUG #ifdef DEBUG
void print_array (int matrix[SIZE][SIZE]) void
print_array(int matrix[SIZE][SIZE])
{ {
int i, j; int i, j;
int first; int first;
for (i = 0; i < SIZE; i++) { for (i = 0; i < SIZE; i++) {
printf ("["); printf("[");
first = 1; first = 1;
for (j = 0; j < SIZE; j++) { for (j = 0; j < SIZE; j++) {
if (!first) if (!first)
printf (","); printf(",");
printf ("%x", matrix[i][j]); printf("%x", matrix[i][j]);
first = 0; first = 0;
}
printf ("]\n");
} }
printf("]\n");
}
} }
#endif #endif
@@ -44,87 +44,83 @@ void print_array (int matrix[SIZE][SIZE])
* is enabled. The loop multiplies the two matrices matrixa * is enabled. The loop multiplies the two matrices matrixa
* and matrixb. * and matrixb.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
int cancel_type, status; int cancel_type, status;
int i, j, k, value = 1; int i, j, k, value = 1;
/*
* Initialize the matrices to something arbitrary.
*/
for (i = 0; i < SIZE; i++)
for (j = 0; j < SIZE; j++) {
matrixa[i][j] = i;
matrixb[i][j] = j;
}
while (1) {
/* /*
* Initialize the matrices to something arbitrary. * Compute the matrix product of matrixa and matrixb.
*/
status = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &cancel_type);
if (status != 0)
err_abort(status, "Set cancel type");
for (i = 0; i < SIZE; i++)
for (j = 0; j < SIZE; j++) {
matrixc[i][j] = 0;
for (k = 0; k < SIZE; k++)
matrixc[i][j] += matrixa[i][k] * matrixb[k][j];
}
status = pthread_setcanceltype(cancel_type, &cancel_type);
if (status != 0)
err_abort(status, "Set cancel type");
/*
* Copy the result (matrixc) into matrixa to start again
*/ */
for (i = 0; i < SIZE; i++) for (i = 0; i < SIZE; i++)
for (j = 0; j < SIZE; j++) { for (j = 0; j < SIZE; j++) matrixa[i][j] = matrixc[i][j];
matrixa[i][j] = i; }
matrixb[i][j] = j;
}
while (1) {
/*
* Compute the matrix product of matrixa and matrixb.
*/
status = pthread_setcanceltype (
PTHREAD_CANCEL_ASYNCHRONOUS,
&cancel_type);
if (status != 0)
err_abort (status, "Set cancel type");
for (i = 0; i < SIZE; i++)
for (j = 0; j < SIZE; j++) {
matrixc[i][j] = 0;
for (k = 0; k < SIZE; k++)
matrixc[i][j] += matrixa[i][k] * matrixb[k][j];
}
status = pthread_setcanceltype (
cancel_type,
&cancel_type);
if (status != 0)
err_abort (status, "Set cancel type");
/*
* Copy the result (matrixc) into matrixa to start again
*/
for (i = 0; i < SIZE; i++)
for (j = 0; j < SIZE; j++)
matrixa[i][j] = matrixc[i][j];
}
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t thread_id; pthread_t thread_id;
void *result; void* result;
int status; int status;
#ifdef sun #ifdef sun
/* /*
* On Solaris 2.5, threads are not timesliced. To ensure * On Solaris 2.5, threads are not timesliced. To ensure
* that our two threads can run concurrently, we need to * that our two threads can run concurrently, we need to
* increase the concurrency level to 2. * increase the concurrency level to 2.
*/ */
DPRINTF (("Setting concurrency level to 2\n")); DPRINTF(("Setting concurrency level to 2\n"));
thr_setconcurrency (2); thr_setconcurrency(2);
#endif #endif
status = pthread_create ( status = pthread_create(&thread_id, NULL, thread_routine, NULL);
&thread_id, NULL, thread_routine, NULL); if (status != 0)
if (status != 0) err_abort(status, "Create thread");
err_abort (status, "Create thread"); sleep(1);
sleep (1); status = pthread_cancel(thread_id);
status = pthread_cancel (thread_id); if (status != 0)
if (status != 0) err_abort(status, "Cancel thread");
err_abort (status, "Cancel thread"); status = pthread_join(thread_id, &result);
status = pthread_join (thread_id, &result); if (status != 0)
if (status != 0) err_abort(status, "Join thread");
err_abort (status, "Join thread"); if (result == PTHREAD_CANCELED)
if (result == PTHREAD_CANCELED) printf("Thread cancelled\n");
printf ("Thread cancelled\n"); else
else printf("Thread was not cancelled\n");
printf ("Thread was not cancelled\n");
#ifdef DEBUG #ifdef DEBUG
printf ("Matrix a:\n"); printf("Matrix a:\n");
print_array (matrixa); print_array(matrixa);
printf ("\nMatrix b:\n"); printf("\nMatrix b:\n");
print_array (matrixb); print_array(matrixb);
printf ("\nMatrix c:\n"); printf("\nMatrix c:\n");
print_array (matrixc); print_array(matrixc);
#endif #endif
return 0; return 0;
} }

View File

@@ -14,29 +14,29 @@
* the synchronization and invariant data. * the synchronization and invariant data.
*/ */
typedef struct control_tag { typedef struct control_tag {
int counter, busy; int counter, busy;
pthread_mutex_t mutex; pthread_mutex_t mutex;
pthread_cond_t cv; pthread_cond_t cv;
} control_t; } control_t;
control_t control = control_t control = {0, 1, PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};
{0, 1, PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};
/* /*
* This routine is installed as the cancellation cleanup * This routine is installed as the cancellation cleanup
* handler around the cancellable condition wait. It will * handler around the cancellable condition wait. It will
* be called by the system when the thread is cancelled. * be called by the system when the thread is cancelled.
*/ */
void cleanup_handler (void *arg) void
cleanup_handler(void* arg)
{ {
control_t *st = (control_t *)arg; control_t* st = (control_t*) arg;
int status; int status;
st->counter--; st->counter--;
printf ("cleanup_handler: counter == %d\n", st->counter); printf("cleanup_handler: counter == %d\n", st->counter);
status = pthread_mutex_unlock (&st->mutex); status = pthread_mutex_unlock(&st->mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock in cleanup handler"); err_abort(status, "Unlock in cleanup handler");
} }
/* /*
@@ -46,55 +46,56 @@ void cleanup_handler (void *arg)
* nonzero value to pthread_cleanup_pop to run the same * nonzero value to pthread_cleanup_pop to run the same
* "finalization" action when cancellation does not occur. * "finalization" action when cancellation does not occur.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
int status; int status;
pthread_cleanup_push (cleanup_handler, (void*)&control); pthread_cleanup_push(cleanup_handler, (void*) &control);
status = pthread_mutex_lock (&control.mutex); status = pthread_mutex_lock(&control.mutex);
if (status != 0)
err_abort(status, "Mutex lock");
control.counter++;
while (control.busy) {
status = pthread_cond_wait(&control.cv, &control.mutex);
if (status != 0) if (status != 0)
err_abort (status, "Mutex lock"); err_abort(status, "Wait on condition");
control.counter++; }
while (control.busy) { pthread_cleanup_pop(1);
status = pthread_cond_wait (&control.cv, &control.mutex); return NULL;
if (status != 0)
err_abort (status, "Wait on condition");
}
pthread_cleanup_pop (1);
return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t thread_id[THREADS]; pthread_t thread_id[THREADS];
int count; int count;
void *result; void* result;
int status; int status;
for (count = 0; count < THREADS; count++) { for (count = 0; count < THREADS; count++) {
status = pthread_create ( status = pthread_create(&thread_id[count], NULL, thread_routine, NULL);
&thread_id[count], NULL, thread_routine, NULL); if (status != 0)
if (status != 0) err_abort(status, "Create thread");
err_abort (status, "Create thread"); }
}
sleep (2); sleep(2);
for (count = 0; count < THREADS; count++) { for (count = 0; count < THREADS; count++) {
status = pthread_cancel (thread_id[count]); status = pthread_cancel(thread_id[count]);
if (status != 0) if (status != 0)
err_abort (status, "Cancel thread"); err_abort(status, "Cancel thread");
status = pthread_join (thread_id[count], &result); status = pthread_join(thread_id[count], &result);
if (status != 0) if (status != 0)
err_abort (status, "Join thread"); err_abort(status, "Join thread");
if (result == PTHREAD_CANCELED) if (result == PTHREAD_CANCELED)
printf ("thread %d cancelled\n", count); printf("thread %d cancelled\n", count);
else else
printf ("thread %d was not cancelled\n", count); printf("thread %d was not cancelled\n", count);
} }
return 0; return 0;
} }

View File

@@ -12,57 +12,55 @@ static int counter;
/* /*
* Thread start routine. * Thread start routine.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
int state; int state;
int status; int status;
for (counter = 0; ; counter++) { for (counter = 0;; counter++) {
/*
/* * Each 755 iterations, disable cancellation and sleep
* Each 755 iterations, disable cancellation and sleep * for one second.
* for one second. *
* * Each 1000 iterations, test for a pending cancel by
* Each 1000 iterations, test for a pending cancel by * calling pthread_testcancel().
* calling pthread_testcancel(). */
*/ if ((counter % 755) == 0) {
if ((counter % 755) == 0) { status = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
status = pthread_setcancelstate ( if (status != 0)
PTHREAD_CANCEL_DISABLE, &state); err_abort(status, "Disable cancel");
if (status != 0) sleep(1);
err_abort (status, "Disable cancel"); status = pthread_setcancelstate(state, &state);
sleep (1); if (status != 0)
status = pthread_setcancelstate ( err_abort(status, "Restore cancel");
state, &state);
if (status != 0)
err_abort (status, "Restore cancel");
} else
if ((counter % 1000) == 0)
pthread_testcancel ();
} }
else if ((counter % 1000) == 0)
pthread_testcancel();
}
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t thread_id; pthread_t thread_id;
void *result; void* result;
int status; int status;
status = pthread_create ( status = pthread_create(&thread_id, NULL, thread_routine, NULL);
&thread_id, NULL, thread_routine, NULL); if (status != 0)
if (status != 0) err_abort(status, "Create thread");
err_abort (status, "Create thread"); sleep(2);
sleep (2); status = pthread_cancel(thread_id);
status = pthread_cancel (thread_id); if (status != 0)
if (status != 0) err_abort(status, "Cancel thread");
err_abort (status, "Cancel thread");
status = pthread_join (thread_id, &result); status = pthread_join(thread_id, &result);
if (status != 0) if (status != 0)
err_abort (status, "Join thread"); err_abort(status, "Join thread");
if (result == PTHREAD_CANCELED) if (result == PTHREAD_CANCELED)
printf ("Thread cancelled at iteration %d\n", counter); printf("Thread cancelled at iteration %d\n", counter);
else else
printf ("Thread was not cancelled\n"); printf("Thread was not cancelled\n");
return 0; return 0;
} }

View File

@@ -18,42 +18,44 @@
* Structure that defines the threads in a "team". * Structure that defines the threads in a "team".
*/ */
typedef struct team_tag { typedef struct team_tag {
int join_i; /* join index */ int join_i; /* join index */
pthread_t workers[THREADS]; /* thread identifiers */ pthread_t workers[THREADS]; /* thread identifiers */
} team_t; } team_t;
/* /*
* Start routine for worker threads. They loop waiting for a * Start routine for worker threads. They loop waiting for a
* cancellation request. * cancellation request.
*/ */
void *worker_routine (void *arg) void*
worker_routine(void* arg)
{ {
int counter; int counter;
for (counter = 0; ; counter++) for (counter = 0;; counter++)
if ((counter % 1000) == 0) if ((counter % 1000) == 0)
pthread_testcancel (); pthread_testcancel();
} }
/* /*
* Cancellation cleanup handler for the contractor thread. It * Cancellation cleanup handler for the contractor thread. It
* will cancel and detach each worker in the team. * will cancel and detach each worker in the team.
*/ */
void cleanup (void *arg) void
cleanup(void* arg)
{ {
team_t *team = (team_t *)arg; team_t* team = (team_t*) arg;
int count, status; int count, status;
for (count = team->join_i; count < THREADS; count++) { for (count = team->join_i; count < THREADS; count++) {
status = pthread_cancel (team->workers[count]); status = pthread_cancel(team->workers[count]);
if (status != 0) if (status != 0)
err_abort (status, "Cancel worker"); err_abort(status, "Cancel worker");
status = pthread_detach (team->workers[count]); status = pthread_detach(team->workers[count]);
if (status != 0) if (status != 0)
err_abort (status, "Detach worker"); err_abort(status, "Detach worker");
printf ("Cleanup: cancelled %d\n", count); printf("Cleanup: cancelled %d\n", count);
} }
} }
/* /*
@@ -61,55 +63,56 @@ void cleanup (void *arg)
* worker threads, and then joins with them. When cancelled, the * worker threads, and then joins with them. When cancelled, the
* cleanup handler will cancel and detach the remaining threads. * cleanup handler will cancel and detach the remaining threads.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
team_t team; /* team info */ team_t team; /* team info */
int count; int count;
void *result; /* Return status */ void* result; /* Return status */
int status; int status;
for (count = 0; count < THREADS; count++) { for (count = 0; count < THREADS; count++) {
status = pthread_create ( status = pthread_create(&team.workers[count], NULL, worker_routine, NULL);
&team.workers[count], NULL, worker_routine, NULL); if (status != 0)
if (status != 0) err_abort(status, "Create worker");
err_abort (status, "Create worker"); }
} pthread_cleanup_push(cleanup, (void*) &team);
pthread_cleanup_push (cleanup, (void*)&team);
for (team.join_i = 0; team.join_i < THREADS; team.join_i++) { for (team.join_i = 0; team.join_i < THREADS; team.join_i++) {
status = pthread_join (team.workers[team.join_i], &result); status = pthread_join(team.workers[team.join_i], &result);
if (status != 0) if (status != 0)
err_abort (status, "Join worker"); err_abort(status, "Join worker");
} }
pthread_cleanup_pop (0); pthread_cleanup_pop(0);
return NULL; return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t thread_id; pthread_t thread_id;
int status; int status;
#ifdef sun #ifdef sun
/* /*
* On Solaris 2.5, threads are not timesliced. To ensure * On Solaris 2.5, threads are not timesliced. To ensure
* that our threads can run concurrently, we need to * that our threads can run concurrently, we need to
* increase the concurrency level to at least 2 plus THREADS * increase the concurrency level to at least 2 plus THREADS
* (the number of workers). * (the number of workers).
*/ */
DPRINTF (("Setting concurrency level to %d\n", THREADS+2)); DPRINTF(("Setting concurrency level to %d\n", THREADS + 2));
thr_setconcurrency (THREADS+2); thr_setconcurrency(THREADS + 2);
#endif #endif
status = pthread_create (&thread_id, NULL, thread_routine, NULL); status = pthread_create(&thread_id, NULL, thread_routine, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Create team"); err_abort(status, "Create team");
sleep (5); sleep(5);
printf ("Cancelling...\n"); printf("Cancelling...\n");
status = pthread_cancel (thread_id); status = pthread_cancel(thread_id);
if (status != 0) if (status != 0)
err_abort (status, "Cancel team"); err_abort(status, "Cancel team");
status = pthread_join (thread_id, NULL); status = pthread_join(thread_id, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Join team"); err_abort(status, "Join team");
} }

View File

@@ -13,22 +13,22 @@
pthread_cond_t cond; pthread_cond_t cond;
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_condattr_t cond_attr; pthread_condattr_t cond_attr;
int status; int status;
status = pthread_condattr_init (&cond_attr); status = pthread_condattr_init(&cond_attr);
if (status != 0) if (status != 0)
err_abort (status, "Create attr"); err_abort(status, "Create attr");
#ifdef _POSIX_THREAD_PROCESS_SHARED #ifdef _POSIX_THREAD_PROCESS_SHARED
status = pthread_condattr_setpshared ( status = pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_PRIVATE);
&cond_attr, PTHREAD_PROCESS_PRIVATE); if (status != 0)
if (status != 0) err_abort(status, "Set pshared");
err_abort (status, "Set pshared");
#endif #endif
status = pthread_cond_init (&cond, &cond_attr); status = pthread_cond_init(&cond, &cond_attr);
if (status != 0) if (status != 0)
err_abort (status, "Init cond"); err_abort(status, "Init cond");
return 0; return 0;
} }

View File

@@ -14,22 +14,22 @@
pthread_mutex_t mutex; pthread_mutex_t mutex;
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_mutexattr_t mutex_attr; pthread_mutexattr_t mutex_attr;
int status; int status;
status = pthread_mutexattr_init (&mutex_attr); status = pthread_mutexattr_init(&mutex_attr);
if (status != 0) if (status != 0)
err_abort (status, "Create attr"); err_abort(status, "Create attr");
#ifdef _POSIX_THREAD_PROCESS_SHARED #ifdef _POSIX_THREAD_PROCESS_SHARED
status = pthread_mutexattr_setpshared ( status = pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_PRIVATE);
&mutex_attr, PTHREAD_PROCESS_PRIVATE); if (status != 0)
if (status != 0) err_abort(status, "Set pshared");
err_abort (status, "Set pshared");
#endif #endif
status = pthread_mutex_init (&mutex, &mutex_attr); status = pthread_mutex_init(&mutex, &mutex_attr);
if (status != 0) if (status != 0)
err_abort (status, "Init mutex"); err_abort(status, "Init mutex");
return 0; return 0;
} }

View File

@@ -16,56 +16,59 @@ pthread_mutex_t mutex;
* with the same control structure are made during the course of * with the same control structure are made during the course of
* the program. * the program.
*/ */
void once_init_routine (void) void
once_init_routine(void)
{ {
int status; int status;
status = pthread_mutex_init (&mutex, NULL); status = pthread_mutex_init(&mutex, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Init Mutex"); err_abort(status, "Init Mutex");
} }
/* /*
* Thread start routine that calls pthread_once. * Thread start routine that calls pthread_once.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
int status; int status;
status = pthread_once (&once_block, once_init_routine); status = pthread_once(&once_block, once_init_routine);
if (status != 0) if (status != 0)
err_abort (status, "Once init"); err_abort(status, "Once init");
status = pthread_mutex_lock (&mutex); status = pthread_mutex_lock(&mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock mutex"); err_abort(status, "Lock mutex");
printf ("thread_routine has locked the mutex.\n"); printf("thread_routine has locked the mutex.\n");
status = pthread_mutex_unlock (&mutex); status = pthread_mutex_unlock(&mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock mutex"); err_abort(status, "Unlock mutex");
return NULL; return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t thread_id; pthread_t thread_id;
char *input, buffer[64]; char *input, buffer[64];
int status; int status;
status = pthread_create (&thread_id, NULL, thread_routine, NULL); status = pthread_create(&thread_id, NULL, thread_routine, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Create thread"); err_abort(status, "Create thread");
status = pthread_once (&once_block, once_init_routine); status = pthread_once(&once_block, once_init_routine);
if (status != 0) if (status != 0)
err_abort (status, "Once init"); err_abort(status, "Once init");
status = pthread_mutex_lock (&mutex); status = pthread_mutex_lock(&mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock mutex"); err_abort(status, "Lock mutex");
printf ("Main has locked the mutex.\n"); printf("Main has locked the mutex.\n");
status = pthread_mutex_unlock (&mutex); status = pthread_mutex_unlock(&mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock mutex"); err_abort(status, "Unlock mutex");
status = pthread_join (thread_id, NULL); status = pthread_join(thread_id, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Join thread"); err_abort(status, "Join thread");
return 0; return 0;
} }

View File

@@ -9,54 +9,56 @@
* _POSIX_THREAD_PRIORITY_SCHEDULING, it does not support the * _POSIX_THREAD_PRIORITY_SCHEDULING, it does not support the
* SCHED_RR policy for threads. * SCHED_RR policy for threads.
*/ */
#include <unistd.h>
#include <pthread.h> #include <pthread.h>
#include <sched.h> #include <sched.h>
#include <unistd.h>
#include "errors.h" #include "errors.h"
/* /*
* Thread start routine. If priority scheduling is supported, * Thread start routine. If priority scheduling is supported,
* report the thread's scheduling attributes. * report the thread's scheduling attributes.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
int my_policy; int my_policy;
struct sched_param my_param; struct sched_param my_param;
int status; int status;
/* /*
* If the priority scheduling option is not defined, then we * If the priority scheduling option is not defined, then we
* can do nothing with the output of pthread_getschedparam, * can do nothing with the output of pthread_getschedparam,
* so just report that the thread ran, and exit. * so just report that the thread ran, and exit.
*/ */
#if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && !defined (sun) #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && !defined(sun)
status = pthread_getschedparam ( status = pthread_getschedparam(pthread_self(), &my_policy, &my_param);
pthread_self (), &my_policy, &my_param); if (status != 0)
if (status != 0) err_abort(status, "Get sched");
err_abort (status, "Get sched"); printf("thread_routine running at %s/%d\n",
printf ("thread_routine running at %s/%d\n", (my_policy == SCHED_FIFO
(my_policy == SCHED_FIFO ? "FIFO" ? "FIFO"
: (my_policy == SCHED_RR ? "RR" : (my_policy == SCHED_RR
: (my_policy == SCHED_OTHER ? "OTHER" ? "RR"
: "unknown"))), : (my_policy == SCHED_OTHER ? "OTHER" : "unknown"))),
my_param.sched_priority); my_param.sched_priority);
#else #else
printf ("thread_routine running\n"); printf("thread_routine running\n");
#endif #endif
return NULL; return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t thread_id; pthread_t thread_id;
pthread_attr_t thread_attr; pthread_attr_t thread_attr;
int thread_policy; int thread_policy;
struct sched_param thread_param; struct sched_param thread_param;
int status, rr_min_priority, rr_max_priority; int status, rr_min_priority, rr_max_priority;
status = pthread_attr_init (&thread_attr); status = pthread_attr_init(&thread_attr);
if (status != 0) if (status != 0)
err_abort (status, "Init attr"); err_abort(status, "Init attr");
/* /*
* If the priority scheduling option is defined, set various scheduling * If the priority scheduling option is defined, set various scheduling
@@ -66,74 +68,64 @@ int main (int argc, char *argv[])
* behavior is to inherit scheduling information from the creating * behavior is to inherit scheduling information from the creating
* thread. * thread.
*/ */
#if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && !defined (sun) #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && !defined(sun)
status = pthread_attr_getschedpolicy ( status = pthread_attr_getschedpolicy(&thread_attr, &thread_policy);
&thread_attr, &thread_policy); if (status != 0)
if (status != 0) err_abort(status, "Get policy");
err_abort (status, "Get policy"); status = pthread_attr_getschedparam(&thread_attr, &thread_param);
status = pthread_attr_getschedparam ( if (status != 0)
&thread_attr, &thread_param); err_abort(status, "Get sched param");
if (status != 0) printf("Default policy is %s, priority is %d\n",
err_abort (status, "Get sched param"); (thread_policy == SCHED_FIFO
printf ( ? "FIFO"
"Default policy is %s, priority is %d\n", : (thread_policy == SCHED_RR
(thread_policy == SCHED_FIFO ? "FIFO" ? "RR"
: (thread_policy == SCHED_RR ? "RR" : (thread_policy == SCHED_OTHER ? "OTHER" : "unknown"))),
: (thread_policy == SCHED_OTHER ? "OTHER" thread_param.sched_priority);
: "unknown"))),
thread_param.sched_priority);
status = pthread_attr_setschedpolicy ( status = pthread_attr_setschedpolicy(&thread_attr, SCHED_RR);
&thread_attr, SCHED_RR); if (status != 0)
printf("Unable to set SCHED_RR policy.\n");
else {
/*
* Just for the sake of the exercise, we'll use the
* middle of the priority range allowed for
* SCHED_RR. This should ensure that the thread will be
* run, without blocking everything else. Because any
* assumptions about how a thread's priority interacts
* with other threads (even in other processes) are
* nonportable, especially on an implementation that
* defaults to System contention scope, you may have to
* adjust this code before it will work on some systems.
*/
rr_min_priority = sched_get_priority_min(SCHED_RR);
if (rr_min_priority == -1)
errno_abort("Get SCHED_RR min priority");
rr_max_priority = sched_get_priority_max(SCHED_RR);
if (rr_max_priority == -1)
errno_abort("Get SCHED_RR max priority");
thread_param.sched_priority = (rr_min_priority + rr_max_priority) / 2;
printf("SCHED_RR priority range is %d to %d: using %d\n",
rr_min_priority,
rr_max_priority,
thread_param.sched_priority);
status = pthread_attr_setschedparam(&thread_attr, &thread_param);
if (status != 0) if (status != 0)
printf ("Unable to set SCHED_RR policy.\n"); err_abort(status, "Set params");
else { printf("Creating thread at RR/%d\n", thread_param.sched_priority);
/* status = pthread_attr_setinheritsched(&thread_attr, PTHREAD_EXPLICIT_SCHED);
* Just for the sake of the exercise, we'll use the if (status != 0)
* middle of the priority range allowed for err_abort(status, "Set inherit");
* SCHED_RR. This should ensure that the thread will be }
* run, without blocking everything else. Because any
* assumptions about how a thread's priority interacts
* with other threads (even in other processes) are
* nonportable, especially on an implementation that
* defaults to System contention scope, you may have to
* adjust this code before it will work on some systems.
*/
rr_min_priority = sched_get_priority_min (SCHED_RR);
if (rr_min_priority == -1)
errno_abort ("Get SCHED_RR min priority");
rr_max_priority = sched_get_priority_max (SCHED_RR);
if (rr_max_priority == -1)
errno_abort ("Get SCHED_RR max priority");
thread_param.sched_priority =
(rr_min_priority + rr_max_priority)/2;
printf (
"SCHED_RR priority range is %d to %d: using %d\n",
rr_min_priority,
rr_max_priority,
thread_param.sched_priority);
status = pthread_attr_setschedparam (
&thread_attr, &thread_param);
if (status != 0)
err_abort (status, "Set params");
printf (
"Creating thread at RR/%d\n",
thread_param.sched_priority);
status = pthread_attr_setinheritsched (
&thread_attr, PTHREAD_EXPLICIT_SCHED);
if (status != 0)
err_abort (status, "Set inherit");
}
#else #else
printf ("Priority scheduling not supported\n"); printf("Priority scheduling not supported\n");
#endif #endif
status = pthread_create ( status = pthread_create(&thread_id, &thread_attr, thread_routine, NULL);
&thread_id, &thread_attr, thread_routine, NULL); if (status != 0)
if (status != 0) err_abort(status, "Create thread");
err_abort (status, "Create thread"); status = pthread_join(thread_id, NULL);
status = pthread_join (thread_id, NULL); if (status != 0)
if (status != 0) err_abort(status, "Join thread");
err_abort (status, "Join thread"); printf("Main exiting\n");
printf ("Main exiting\n"); return 0;
return 0;
} }

View File

@@ -6,9 +6,9 @@
* Special note: This demonstration will fail on Solaris 2.5 * Special note: This demonstration will fail on Solaris 2.5
* because it does not implement SCHED_RR. * because it does not implement SCHED_RR.
*/ */
#include <unistd.h>
#include <pthread.h> #include <pthread.h>
#include <sched.h> #include <sched.h>
#include <unistd.h>
#include "errors.h" #include "errors.h"
#define THREADS 5 #define THREADS 5
@@ -17,72 +17,72 @@
* Structure describing each thread. * Structure describing each thread.
*/ */
typedef struct thread_tag { typedef struct thread_tag {
int index; int index;
pthread_t id; pthread_t id;
} thread_t; } thread_t;
thread_t threads[THREADS]; thread_t threads[THREADS];
int rr_min_priority; int rr_min_priority;
/* /*
* Thread start routine that will set its own priority * Thread start routine that will set its own priority
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
thread_t *self = (thread_t*)arg; thread_t* self = (thread_t*) arg;
int my_policy; int my_policy;
struct sched_param my_param; struct sched_param my_param;
int status; int status;
my_param.sched_priority = rr_min_priority + self->index; my_param.sched_priority = rr_min_priority + self->index;
DPRINTF (( DPRINTF(("Thread %d will set SCHED_FIFO, priority %d\n",
"Thread %d will set SCHED_FIFO, priority %d\n", self->index,
self->index, my_param.sched_priority)); my_param.sched_priority));
status = pthread_setschedparam ( status = pthread_setschedparam(self->id, SCHED_RR, &my_param);
self->id, SCHED_RR, &my_param); if (status != 0)
if (status != 0) err_abort(status, "Set sched");
err_abort (status, "Set sched"); status = pthread_getschedparam(self->id, &my_policy, &my_param);
status = pthread_getschedparam ( if (status != 0)
self->id, &my_policy, &my_param); err_abort(status, "Get sched");
if (status != 0) printf("thread_routine %d running at %s/%d\n",
err_abort (status, "Get sched"); self->index,
printf ("thread_routine %d running at %s/%d\n", (my_policy == SCHED_FIFO
self->index, ? "FIFO"
(my_policy == SCHED_FIFO ? "FIFO" : (my_policy == SCHED_RR
: (my_policy == SCHED_RR ? "RR" ? "RR"
: (my_policy == SCHED_OTHER ? "OTHER" : (my_policy == SCHED_OTHER ? "OTHER" : "unknown"))),
: "unknown"))), my_param.sched_priority);
my_param.sched_priority); return NULL;
return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
int count, status; int count, status;
rr_min_priority = sched_get_priority_min (SCHED_RR); rr_min_priority = sched_get_priority_min(SCHED_RR);
if (rr_min_priority == -1) { if (rr_min_priority == -1) {
#ifdef sun #ifdef sun
if (errno == ENOSYS) { if (errno == ENOSYS) {
fprintf (stderr, "SCHED_RR is not supported.\n"); fprintf(stderr, "SCHED_RR is not supported.\n");
exit (0); exit(0);
} }
#endif #endif
errno_abort ("Get SCHED_RR min priority"); errno_abort("Get SCHED_RR min priority");
} }
for (count = 0; count < THREADS; count++) { for (count = 0; count < THREADS; count++) {
threads[count].index = count; threads[count].index = count;
status = pthread_create ( status = pthread_create(
&threads[count].id, NULL, &threads[count].id, NULL, thread_routine, (void*) &threads[count]);
thread_routine, (void*)&threads[count]); if (status != 0)
if (status != 0) err_abort(status, "Create thread");
err_abort (status, "Create thread"); }
} for (count = 0; count < THREADS; count++) {
for (count = 0; count < THREADS; count++) { status = pthread_join(threads[count].id, NULL);
status = pthread_join (threads[count].id, NULL); if (status != 0)
if (status != 0) err_abort(status, "Join thread");
err_abort (status, "Join thread"); }
} printf("Main exiting\n");
printf ("Main exiting\n"); return 0;
return 0;
} }

View File

@@ -14,56 +14,56 @@
/* /*
* Thread start routine that reports it ran, and then exits. * Thread start routine that reports it ran, and then exits.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
printf ("The thread is here\n"); printf("The thread is here\n");
return NULL; return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t thread_id; pthread_t thread_id;
pthread_attr_t thread_attr; pthread_attr_t thread_attr;
struct sched_param thread_param; struct sched_param thread_param;
size_t stack_size; size_t stack_size;
int status; int status;
status = pthread_attr_init (&thread_attr); status = pthread_attr_init(&thread_attr);
if (status != 0) if (status != 0)
err_abort (status, "Create attr"); err_abort(status, "Create attr");
/* /*
* Create a detached thread. * Create a detached thread.
*/ */
status = pthread_attr_setdetachstate ( status = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
&thread_attr, PTHREAD_CREATE_DETACHED); if (status != 0)
if (status != 0) err_abort(status, "Set detach");
err_abort (status, "Set detach");
#ifdef _POSIX_THREAD_ATTR_STACKSIZE #ifdef _POSIX_THREAD_ATTR_STACKSIZE
/* /*
* If supported, determine the default stack size and report * If supported, determine the default stack size and report
* it, and then select a stack size for the new thread. * it, and then select a stack size for the new thread.
* *
* Note that the standard does not specify the default stack * Note that the standard does not specify the default stack
* size, and the default value in an attributes object need * size, and the default value in an attributes object need
* not be the size that will actually be used. Solaris 2.5 * not be the size that will actually be used. Solaris 2.5
* uses a value of 0 to indicate the default. * uses a value of 0 to indicate the default.
*/ */
status = pthread_attr_getstacksize (&thread_attr, &stack_size); status = pthread_attr_getstacksize(&thread_attr, &stack_size);
if (status != 0) if (status != 0)
err_abort (status, "Get stack size"); err_abort(status, "Get stack size");
printf ("Default stack size is %u; minimum is %u\n", printf("Default stack size is %u; minimum is %u\n",
stack_size, PTHREAD_STACK_MIN); stack_size,
status = pthread_attr_setstacksize ( PTHREAD_STACK_MIN);
&thread_attr, PTHREAD_STACK_MIN*2); status = pthread_attr_setstacksize(&thread_attr, PTHREAD_STACK_MIN * 2);
if (status != 0) if (status != 0)
err_abort (status, "Set stack size"); err_abort(status, "Set stack size");
#endif #endif
status = pthread_create ( status = pthread_create(&thread_id, &thread_attr, thread_routine, NULL);
&thread_id, &thread_attr, thread_routine, NULL); if (status != 0)
if (status != 0) err_abort(status, "Create thread");
err_abort (status, "Create thread"); printf("Main exiting\n");
printf ("Main exiting\n"); pthread_exit(NULL);
pthread_exit (NULL); return 0;
return 0;
} }

View File

@@ -10,13 +10,13 @@
* Structure used as value of thread-specific data key. * Structure used as value of thread-specific data key.
*/ */
typedef struct private_tag { typedef struct private_tag {
pthread_t thread_id; pthread_t thread_id;
char *string; char* string;
} private_t; } private_t;
pthread_key_t identity_key; /* Thread-specific data key */ pthread_key_t identity_key; /* Thread-specific data key */
pthread_mutex_t identity_key_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t identity_key_mutex = PTHREAD_MUTEX_INITIALIZER;
long identity_key_counter = 0; long identity_key_counter = 0;
/* /*
* This routine is called as each thread terminates with a value * This routine is called as each thread terminates with a value
@@ -24,95 +24,97 @@ long identity_key_counter = 0;
* threads still have values, and deletes the key when there are * threads still have values, and deletes the key when there are
* no more references. * no more references.
*/ */
void identity_key_destructor (void *value) void
identity_key_destructor(void* value)
{ {
private_t *private = (private_t*)value; private_t* private = (private_t*) value;
int status; int status;
printf ("thread \"%s\" exiting...\n", private->string); printf("thread \"%s\" exiting...\n", private->string);
free (value); free(value);
status = pthread_mutex_lock (&identity_key_mutex); status = pthread_mutex_lock(&identity_key_mutex);
if (status != 0)
err_abort(status, "Lock key mutex");
identity_key_counter--;
if (identity_key_counter <= 0) {
status = pthread_key_delete(identity_key);
if (status != 0) if (status != 0)
err_abort (status, "Lock key mutex"); err_abort(status, "Delete key");
identity_key_counter--; printf("key deleted...\n");
if (identity_key_counter <= 0) { }
status = pthread_key_delete (identity_key); status = pthread_mutex_unlock(&identity_key_mutex);
if (status != 0) if (status != 0)
err_abort (status, "Delete key"); err_abort(status, "Unlock key mutex");
printf ("key deleted...\n");
}
status = pthread_mutex_unlock (&identity_key_mutex);
if (status != 0)
err_abort (status, "Unlock key mutex");
} }
/* /*
* Helper routine to allocate a new value for thread-specific * Helper routine to allocate a new value for thread-specific
* data key if the thread doesn't already have one. * data key if the thread doesn't already have one.
*/ */
void *identity_key_get (void) void*
identity_key_get(void)
{ {
void *value; void* value;
int status; int status;
value = pthread_getspecific (identity_key); value = pthread_getspecific(identity_key);
if (value == NULL) { if (value == NULL) {
value = malloc (sizeof (private_t)); value = malloc(sizeof(private_t));
if (value == NULL) if (value == NULL)
errno_abort ("Allocate key value"); errno_abort("Allocate key value");
status = pthread_setspecific (identity_key, (void*)value); status = pthread_setspecific(identity_key, (void*) value);
if (status != 0) if (status != 0)
err_abort (status, "Set TSD"); err_abort(status, "Set TSD");
} }
return value; return value;
} }
/* /*
* Thread start routine to use thread-specific data. * Thread start routine to use thread-specific data.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
private_t *value; private_t* value;
value = (private_t*)identity_key_get (); value = (private_t*) identity_key_get();
value->thread_id = pthread_self (); value->thread_id = pthread_self();
value->string = (char*)arg; value->string = (char*) arg;
printf ("thread \"%s\" starting...\n", value->string); printf("thread \"%s\" starting...\n", value->string);
sleep (2); sleep(2);
return NULL; return NULL;
} }
void main (int argc, char *argv[]) void
main(int argc, char* argv[])
{ {
pthread_t thread_1, thread_2; pthread_t thread_1, thread_2;
private_t *value; private_t* value;
int status; int status;
/* /*
* Create the TSD key, and set the reference counter to * Create the TSD key, and set the reference counter to
* the number of threads that will use it (two thread_routine * the number of threads that will use it (two thread_routine
* threads plus main). This must be done before creating * threads plus main). This must be done before creating
* the threads! Otherwise, if one thread runs the key's * the threads! Otherwise, if one thread runs the key's
* destructor before any other thread uses the key, it will * destructor before any other thread uses the key, it will
* be deleted. * be deleted.
* *
* Note that there's rarely any good reason to delete a * Note that there's rarely any good reason to delete a
* thread-specific data key. * thread-specific data key.
*/ */
status = pthread_key_create (&identity_key, identity_key_destructor); status = pthread_key_create(&identity_key, identity_key_destructor);
if (status != 0) if (status != 0)
err_abort (status, "Create key"); err_abort(status, "Create key");
identity_key_counter = 3; identity_key_counter = 3;
value = (private_t*)identity_key_get (); value = (private_t*) identity_key_get();
value->thread_id = pthread_self (); value->thread_id = pthread_self();
value->string = "Main thread"; value->string = "Main thread";
status = pthread_create (&thread_1, NULL, status = pthread_create(&thread_1, NULL, thread_routine, "Thread 1");
thread_routine, "Thread 1"); if (status != 0)
if (status != 0) err_abort(status, "Create thread 1");
err_abort (status, "Create thread 1"); status = pthread_create(&thread_2, NULL, thread_routine, "Thread 2");
status = pthread_create (&thread_2, NULL, if (status != 0)
thread_routine, "Thread 2"); err_abort(status, "Create thread 2");
if (status != 0) pthread_exit(NULL);
err_abort (status, "Create thread 2");
pthread_exit (NULL);
} }

View File

@@ -14,68 +14,69 @@
* Structure used as the value for thread-specific data key. * Structure used as the value for thread-specific data key.
*/ */
typedef struct tsd_tag { typedef struct tsd_tag {
pthread_t thread_id; pthread_t thread_id;
char *string; char* string;
} tsd_t; } tsd_t;
pthread_key_t tsd_key; /* Thread-specific data key */ pthread_key_t tsd_key; /* Thread-specific data key */
pthread_once_t key_once = PTHREAD_ONCE_INIT; pthread_once_t key_once = PTHREAD_ONCE_INIT;
/* /*
* One-time initialization routine used with the pthread_once * One-time initialization routine used with the pthread_once
* control block. * control block.
*/ */
void once_routine (void) void
once_routine(void)
{ {
int status; int status;
printf ("initializing key\n"); printf("initializing key\n");
status = pthread_key_create (&tsd_key, NULL); status = pthread_key_create(&tsd_key, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Create key"); err_abort(status, "Create key");
} }
/* /*
* Thread start routine that uses pthread_once to dynamically * Thread start routine that uses pthread_once to dynamically
* create a thread-specific data key. * create a thread-specific data key.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
tsd_t *value; tsd_t* value;
int status; int status;
status = pthread_once (&key_once, once_routine); status = pthread_once(&key_once, once_routine);
if (status != 0) if (status != 0)
err_abort (status, "Once init"); err_abort(status, "Once init");
value = (tsd_t*)malloc (sizeof (tsd_t)); value = (tsd_t*) malloc(sizeof(tsd_t));
if (value == NULL) if (value == NULL)
errno_abort ("Allocate key value"); errno_abort("Allocate key value");
status = pthread_setspecific (tsd_key, value); status = pthread_setspecific(tsd_key, value);
if (status != 0) if (status != 0)
err_abort (status, "Set tsd"); err_abort(status, "Set tsd");
printf ("%s set tsd value %p\n", arg, value); printf("%s set tsd value %p\n", arg, value);
value->thread_id = pthread_self (); value->thread_id = pthread_self();
value->string = (char*)arg; value->string = (char*) arg;
value = (tsd_t*)pthread_getspecific (tsd_key); value = (tsd_t*) pthread_getspecific(tsd_key);
printf ("%s starting...\n", value->string); printf("%s starting...\n", value->string);
sleep (2); sleep(2);
value = (tsd_t*)pthread_getspecific (tsd_key); value = (tsd_t*) pthread_getspecific(tsd_key);
printf ("%s done...\n", value->string); printf("%s done...\n", value->string);
return NULL; return NULL;
} }
void main (int argc, char *argv[]) void
main(int argc, char* argv[])
{ {
pthread_t thread1, thread2; pthread_t thread1, thread2;
int status; int status;
status = pthread_create ( status = pthread_create(&thread1, NULL, thread_routine, "thread 1");
&thread1, NULL, thread_routine, "thread 1"); if (status != 0)
if (status != 0) err_abort(status, "Create thread 1");
err_abort (status, "Create thread 1"); status = pthread_create(&thread2, NULL, thread_routine, "thread 2");
status = pthread_create ( if (status != 0)
&thread2, NULL, thread_routine, "thread 2"); err_abort(status, "Create thread 2");
if (status != 0) pthread_exit(NULL);
err_abort (status, "Create thread 2");
pthread_exit (NULL);
} }

View File

@@ -4,127 +4,132 @@
* Demonstrate the use of "fork handlers" to protect data * Demonstrate the use of "fork handlers" to protect data
* invariants across a fork. * invariants across a fork.
*/ */
#include <sys/types.h>
#include <pthread.h> #include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include "errors.h" #include "errors.h"
pid_t self_pid; /* pid of current process */ pid_t self_pid; /* pid of current process */
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
/* /*
* This routine will be called prior to executing the fork, * This routine will be called prior to executing the fork,
* within the parent process. * within the parent process.
*/ */
void fork_prepare (void) void
fork_prepare(void)
{ {
int status; int status;
/* /*
* Lock the mutex in the parent before creating the child, * Lock the mutex in the parent before creating the child,
* to ensure that no other thread can lock it (or change any * to ensure that no other thread can lock it (or change any
* associated shared state) until after the fork completes. * associated shared state) until after the fork completes.
*/ */
status = pthread_mutex_lock (&mutex); status = pthread_mutex_lock(&mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock in prepare handler"); err_abort(status, "Lock in prepare handler");
} }
/* /*
* This routine will be called after executing the fork, within * This routine will be called after executing the fork, within
* the parent process * the parent process
*/ */
void fork_parent (void) void
fork_parent(void)
{ {
int status; int status;
/* /*
* Unlock the mutex in the parent after the child has been created. * Unlock the mutex in the parent after the child has been created.
*/ */
status = pthread_mutex_unlock (&mutex); status = pthread_mutex_unlock(&mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock in parent handler"); err_abort(status, "Unlock in parent handler");
} }
/* /*
* This routine will be called after executing the fork, within * This routine will be called after executing the fork, within
* the child process * the child process
*/ */
void fork_child (void) void
fork_child(void)
{ {
int status; int status;
/* /*
* Update the file scope "self_pid" within the child process, and unlock * Update the file scope "self_pid" within the child process, and unlock
* the mutex. * the mutex.
*/ */
self_pid = getpid (); self_pid = getpid();
status = pthread_mutex_unlock (&mutex); status = pthread_mutex_unlock(&mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock in child handler"); err_abort(status, "Unlock in child handler");
} }
/* /*
* Thread start routine, which will fork a new child process. * Thread start routine, which will fork a new child process.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
pid_t child_pid; pid_t child_pid;
int status; int status;
child_pid = fork (); child_pid = fork();
if (child_pid == (pid_t)-1) if (child_pid == (pid_t) -1)
errno_abort ("Fork"); errno_abort("Fork");
/* /*
* Lock the mutex -- without the atfork handlers, the mutex will remain * Lock the mutex -- without the atfork handlers, the mutex will remain
* locked in the child process and this lock attempt will hang (or fail * locked in the child process and this lock attempt will hang (or fail
* with EDEADLK) in the child. * with EDEADLK) in the child.
*/ */
status = pthread_mutex_lock (&mutex); status = pthread_mutex_lock(&mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock in child"); err_abort(status, "Lock in child");
status = pthread_mutex_unlock (&mutex); status = pthread_mutex_unlock(&mutex);
if (status != 0) if (status != 0)
err_abort (status, "Unlock in child"); err_abort(status, "Unlock in child");
printf ("After fork: %d (%d)\n", child_pid, self_pid); printf("After fork: %d (%d)\n", child_pid, self_pid);
if (child_pid != 0) { if (child_pid != 0) {
if ((pid_t)-1 == waitpid (child_pid, (int*)0, 0)) if ((pid_t) -1 == waitpid(child_pid, (int*) 0, 0))
errno_abort ("Wait for child"); errno_abort("Wait for child");
} }
return NULL; return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t fork_thread; pthread_t fork_thread;
int atfork_flag = 1; int atfork_flag = 1;
int status; int status;
if (argc > 1) if (argc > 1)
atfork_flag = atoi (argv[1]); atfork_flag = atoi(argv[1]);
if (atfork_flag) { if (atfork_flag) {
status = pthread_atfork (fork_prepare, fork_parent, fork_child); status = pthread_atfork(fork_prepare, fork_parent, fork_child);
if (status != 0)
err_abort (status, "Register fork handlers");
}
self_pid = getpid ();
status = pthread_mutex_lock (&mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock mutex"); err_abort(status, "Register fork handlers");
/* }
* Create a thread while the mutex is locked. It will fork a process, self_pid = getpid();
* which (without atfork handlers) will run with the mutex locked. status = pthread_mutex_lock(&mutex);
*/ if (status != 0)
status = pthread_create (&fork_thread, NULL, thread_routine, NULL); err_abort(status, "Lock mutex");
if (status != 0) /*
err_abort (status, "Create thread"); * Create a thread while the mutex is locked. It will fork a process,
sleep (5); * which (without atfork handlers) will run with the mutex locked.
status = pthread_mutex_unlock (&mutex); */
if (status != 0) status = pthread_create(&fork_thread, NULL, thread_routine, NULL);
err_abort (status, "Unlock mutex"); if (status != 0)
status = pthread_join (fork_thread, NULL); err_abort(status, "Create thread");
if (status != 0) sleep(5);
err_abort (status, "Join thread"); status = pthread_mutex_unlock(&mutex);
return 0; if (status != 0)
err_abort(status, "Unlock mutex");
status = pthread_join(fork_thread, NULL);
if (status != 0)
err_abort(status, "Join thread");
return 0;
} }

View File

@@ -14,71 +14,70 @@
* is prevented by the file locks until both prompt and fgets are * is prevented by the file locks until both prompt and fgets are
* complete. * complete.
*/ */
void *prompt_routine (void *arg) void*
prompt_routine(void* arg)
{ {
char *prompt = (char*)arg; char* prompt = (char*) arg;
char *string; char* string;
int len; int len;
string = (char*)malloc (128); string = (char*) malloc(128);
if (string == NULL) if (string == NULL)
errno_abort ("Alloc string"); errno_abort("Alloc string");
flockfile (stdin); flockfile(stdin);
flockfile (stdout); flockfile(stdout);
printf (prompt); printf(prompt);
if (fgets (string, 128, stdin) == NULL) if (fgets(string, 128, stdin) == NULL)
string[0] = '\0'; string[0] = '\0';
else { else {
len = strlen (string); len = strlen(string);
if (len > 0 && string[len-1] == '\n') if (len > 0 && string[len - 1] == '\n')
string[len-1] = '\0'; string[len - 1] = '\0';
} }
funlockfile (stdout); funlockfile(stdout);
funlockfile (stdin); funlockfile(stdin);
return (void*)string; return (void*) string;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t thread1, thread2, thread3; pthread_t thread1, thread2, thread3;
char *string; char* string;
int status; int status;
#ifdef sun #ifdef sun
/* /*
* On Solaris 2.5, threads are not timesliced. To ensure * On Solaris 2.5, threads are not timesliced. To ensure
* that our threads can run concurrently, we need to * that our threads can run concurrently, we need to
* increase the concurrency level. * increase the concurrency level.
*/ */
DPRINTF (("Setting concurrency level to 4\n")); DPRINTF(("Setting concurrency level to 4\n"));
thr_setconcurrency (4); thr_setconcurrency(4);
#endif #endif
status = pthread_create ( status = pthread_create(&thread1, NULL, prompt_routine, "Thread 1> ");
&thread1, NULL, prompt_routine, "Thread 1> "); if (status != 0)
if (status != 0) err_abort(status, "Create thread");
err_abort (status, "Create thread"); status = pthread_create(&thread2, NULL, prompt_routine, "Thread 2> ");
status = pthread_create ( if (status != 0)
&thread2, NULL, prompt_routine, "Thread 2> "); err_abort(status, "Create thread");
if (status != 0) status = pthread_create(&thread3, NULL, prompt_routine, "Thread 3> ");
err_abort (status, "Create thread"); if (status != 0)
status = pthread_create ( err_abort(status, "Create thread");
&thread3, NULL, prompt_routine, "Thread 3> "); status = pthread_join(thread1, (void**) &string);
if (status != 0) if (status != 0)
err_abort (status, "Create thread"); err_abort(status, "Join thread");
status = pthread_join (thread1, (void**)&string); printf("Thread 1: \"%s\"\n", string);
if (status != 0) free(string);
err_abort (status, "Join thread"); status = pthread_join(thread2, (void**) &string);
printf ("Thread 1: \"%s\"\n", string); if (status != 0)
free (string); err_abort(status, "Join thread");
status = pthread_join (thread2, (void**)&string); printf("Thread 1: \"%s\"\n", string);
if (status != 0) free(string);
err_abort (status, "Join thread"); status = pthread_join(thread3, (void**) &string);
printf ("Thread 1: \"%s\"\n", string); if (status != 0)
free (string); err_abort(status, "Join thread");
status = pthread_join (thread3, (void**)&string); printf("Thread 1: \"%s\"\n", string);
if (status != 0) free(string);
err_abort (status, "Join thread"); return 0;
printf ("Thread 1: \"%s\"\n", string);
free (string);
return 0;
} }

View File

@@ -14,29 +14,29 @@
* and dynamically allocating the buffers. * and dynamically allocating the buffers.
*/ */
#ifndef TTY_NAME_MAX #ifndef TTY_NAME_MAX
# define TTY_NAME_MAX 128 #define TTY_NAME_MAX 128
#endif #endif
#ifndef LOGIN_NAME_MAX #ifndef LOGIN_NAME_MAX
# define LOGIN_NAME_MAX 32 #define LOGIN_NAME_MAX 32
#endif #endif
int main (int argc, char *argv[])
{
char login_str[LOGIN_NAME_MAX];
char stdin_str[TTY_NAME_MAX];
char cterm_str[L_ctermid], *cterm_str_ptr;
int status;
status = getlogin_r (login_str, sizeof (login_str)); int
if (status != 0) main(int argc, char* argv[])
err_abort (status, "Get login"); {
cterm_str_ptr = ctermid (cterm_str); char login_str[LOGIN_NAME_MAX];
if (cterm_str_ptr == NULL) char stdin_str[TTY_NAME_MAX];
errno_abort ("Get cterm"); char cterm_str[L_ctermid], *cterm_str_ptr;
status = ttyname_r (0, stdin_str, sizeof (stdin_str)); int status;
if (status != 0)
err_abort (status, "Get stdin"); status = getlogin_r(login_str, sizeof(login_str));
printf ("User: %s, cterm: %s, fd 0: %s\n", if (status != 0)
login_str, cterm_str, stdin_str); err_abort(status, "Get login");
return 0; cterm_str_ptr = ctermid(cterm_str);
if (cterm_str_ptr == NULL)
errno_abort("Get cterm");
status = ttyname_r(0, stdin_str, sizeof(stdin_str));
if (status != 0)
err_abort(status, "Get stdin");
printf("User: %s, cterm: %s, fd 0: %s\n", login_str, cterm_str, stdin_str);
return 0;
} }

View File

@@ -37,17 +37,18 @@
* by locking the file stream and using putchar_unlocked to * by locking the file stream and using putchar_unlocked to
* write each character individually. * write each character individually.
*/ */
void *lock_routine (void *arg) void*
lock_routine(void* arg)
{ {
char *pointer; char* pointer;
flockfile (stdout); flockfile(stdout);
for (pointer = arg; *pointer != '\0'; pointer++) { for (pointer = arg; *pointer != '\0'; pointer++) {
putchar_unlocked (*pointer); putchar_unlocked(*pointer);
sleep (1); sleep(1);
} }
funlockfile (stdout); funlockfile(stdout);
return NULL; return NULL;
} }
/* /*
@@ -56,41 +57,40 @@ void *lock_routine (void *arg)
* Although the internal locking of putchar prevents file stream * Although the internal locking of putchar prevents file stream
* corruption, the writes of various threads may be interleaved. * corruption, the writes of various threads may be interleaved.
*/ */
void *unlock_routine (void *arg) void*
unlock_routine(void* arg)
{ {
char *pointer; char* pointer;
for (pointer = arg; *pointer != '\0'; pointer++) { for (pointer = arg; *pointer != '\0'; pointer++) {
putchar (*pointer); putchar(*pointer);
sleep (1); sleep(1);
} }
return NULL; return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t thread1, thread2, thread3; pthread_t thread1, thread2, thread3;
int flock_flag = 1; int flock_flag = 1;
void *(*thread_func)(void *); void* (*thread_func)(void*);
int status; int status;
if (argc > 1) if (argc > 1)
flock_flag = atoi (argv[1]); flock_flag = atoi(argv[1]);
if (flock_flag) if (flock_flag)
thread_func = lock_routine; thread_func = lock_routine;
else else
thread_func = unlock_routine; thread_func = unlock_routine;
status = pthread_create ( status = pthread_create(&thread1, NULL, thread_func, "this is thread 1\n");
&thread1, NULL, thread_func, "this is thread 1\n"); if (status != 0)
if (status != 0) err_abort(status, "Create thread");
err_abort (status, "Create thread"); status = pthread_create(&thread2, NULL, thread_func, "this is thread 2\n");
status = pthread_create ( if (status != 0)
&thread2, NULL, thread_func, "this is thread 2\n"); err_abort(status, "Create thread");
if (status != 0) status = pthread_create(&thread3, NULL, thread_func, "this is thread 3\n");
err_abort (status, "Create thread"); if (status != 0)
status = pthread_create ( err_abort(status, "Create thread");
&thread3, NULL, thread_func, "this is thread 3\n"); pthread_exit(NULL);
if (status != 0)
err_abort (status, "Create thread");
pthread_exit (NULL);
} }

View File

@@ -8,109 +8,111 @@
* and _POSIX_SEMAPHORES features of POSIX 1003.1b-1993. It will * and _POSIX_SEMAPHORES features of POSIX 1003.1b-1993. It will
* not run (and may not compile) without them. * not run (and may not compile) without them.
*/ */
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h> #include <pthread.h>
#include <semaphore.h> #include <semaphore.h>
#include <signal.h> #include <signal.h>
#include <sys/types.h>
#include <time.h> #include <time.h>
#include <unistd.h>
#include "errors.h" #include "errors.h"
sem_t semaphore; sem_t semaphore;
/* /*
* Signal catching function. * Signal catching function.
*/ */
void signal_catcher (int sig) void
signal_catcher(int sig)
{ {
if (sem_post (&semaphore) == -1) if (sem_post(&semaphore) == -1)
errno_abort ("Post semaphore"); errno_abort("Post semaphore");
} }
/* /*
* Thread start routine which waits on the semaphore. * Thread start routine which waits on the semaphore.
*/ */
void *sem_waiter (void *arg) void*
sem_waiter(void* arg)
{ {
int number = (int)arg; int number = (int) arg;
int counter; int counter;
/* /*
* Each thread waits 5 times. * Each thread waits 5 times.
*/ */
for (counter = 1; counter <= 5; counter++) { for (counter = 1; counter <= 5; counter++) {
while (sem_wait (&semaphore) == -1) { while (sem_wait(&semaphore) == -1) {
if (errno != EINTR) if (errno != EINTR)
errno_abort ("Wait on semaphore"); errno_abort("Wait on semaphore");
}
printf ("%d waking (%d)...\n", number, counter);
} }
return NULL; printf("%d waking (%d)...\n", number, counter);
}
return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
int thread_count, status; int thread_count, status;
struct sigevent sig_event; struct sigevent sig_event;
struct sigaction sig_action; struct sigaction sig_action;
sigset_t sig_mask; sigset_t sig_mask;
timer_t timer_id; timer_t timer_id;
struct itimerspec timer_val; struct itimerspec timer_val;
pthread_t sem_waiters[5]; pthread_t sem_waiters[5];
#if !defined(_POSIX_SEMAPHORES) || !defined(_POSIX_TIMERS) #if !defined(_POSIX_SEMAPHORES) || !defined(_POSIX_TIMERS)
# if !defined(_POSIX_SEMAPHORES) #if !defined(_POSIX_SEMAPHORES)
printf ("This system does not support POSIX semaphores\n"); printf("This system does not support POSIX semaphores\n");
# endif #endif
# if !defined(_POSIX_TIMERS) #if !defined(_POSIX_TIMERS)
printf ("This system does not support POSIX timers\n"); printf("This system does not support POSIX timers\n");
# endif #endif
return -1; return -1;
#else #else
sem_init (&semaphore, 0, 0); sem_init(&semaphore, 0, 0);
/* /*
* Create 5 threads to wait on a semaphore. * Create 5 threads to wait on a semaphore.
*/ */
for (thread_count = 0; thread_count < 5; thread_count++) { for (thread_count = 0; thread_count < 5; thread_count++) {
status = pthread_create ( status = pthread_create(
&sem_waiters[thread_count], NULL, &sem_waiters[thread_count], NULL, sem_waiter, (void*) thread_count);
sem_waiter, (void*)thread_count); if (status != 0)
if (status != 0) err_abort(status, "Create thread");
err_abort (status, "Create thread"); }
}
/* /*
* Set up a repeating timer using signal number SIGRTMIN, * Set up a repeating timer using signal number SIGRTMIN,
* set to occur every 2 seconds. * set to occur every 2 seconds.
*/ */
sig_event.sigev_value.sival_int = 0; sig_event.sigev_value.sival_int = 0;
sig_event.sigev_signo = SIGRTMIN; sig_event.sigev_signo = SIGRTMIN;
sig_event.sigev_notify = SIGEV_SIGNAL; sig_event.sigev_notify = SIGEV_SIGNAL;
if (timer_create (CLOCK_REALTIME, &sig_event, &timer_id) == -1) if (timer_create(CLOCK_REALTIME, &sig_event, &timer_id) == -1)
errno_abort ("Create timer"); errno_abort("Create timer");
sigemptyset (&sig_mask); sigemptyset(&sig_mask);
sigaddset (&sig_mask, SIGRTMIN); sigaddset(&sig_mask, SIGRTMIN);
sig_action.sa_handler = signal_catcher; sig_action.sa_handler = signal_catcher;
sig_action.sa_mask = sig_mask; sig_action.sa_mask = sig_mask;
sig_action.sa_flags = 0; sig_action.sa_flags = 0;
if (sigaction (SIGRTMIN, &sig_action, NULL) == -1) if (sigaction(SIGRTMIN, &sig_action, NULL) == -1)
errno_abort ("Set signal action"); errno_abort("Set signal action");
timer_val.it_interval.tv_sec = 2; timer_val.it_interval.tv_sec = 2;
timer_val.it_interval.tv_nsec = 0; timer_val.it_interval.tv_nsec = 0;
timer_val.it_value.tv_sec = 2; timer_val.it_value.tv_sec = 2;
timer_val.it_value.tv_nsec = 0; timer_val.it_value.tv_nsec = 0;
if (timer_settime (timer_id, 0, &timer_val, NULL) == -1) if (timer_settime(timer_id, 0, &timer_val, NULL) == -1)
errno_abort ("Set timer"); errno_abort("Set timer");
/* /*
* Wait for all threads to complete. * Wait for all threads to complete.
*/ */
for (thread_count = 0; thread_count < 5; thread_count++) { for (thread_count = 0; thread_count < 5; thread_count++) {
status = pthread_join (sem_waiters[thread_count], NULL); status = pthread_join(sem_waiters[thread_count], NULL);
if (status != 0) if (status != 0)
err_abort (status, "Join thread"); err_abort(status, "Join thread");
} }
return 0; return 0;
#endif #endif
} }

View File

@@ -3,11 +3,11 @@
* *
* Demonstrate use of semaphores for synchronization. * Demonstrate use of semaphores for synchronization.
*/ */
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h> #include <pthread.h>
#include <semaphore.h> #include <semaphore.h>
#include <signal.h> #include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "errors.h" #include "errors.h"
sem_t semaphore; sem_t semaphore;
@@ -15,68 +15,69 @@ sem_t semaphore;
/* /*
* Thread start routine to wait on a semaphore. * Thread start routine to wait on a semaphore.
*/ */
void *sem_waiter (void *arg) void*
sem_waiter(void* arg)
{ {
long num = (long)arg; long num = (long) arg;
printf ("Thread %d waiting\n", num); printf("Thread %d waiting\n", num);
if (sem_wait (&semaphore) == -1) if (sem_wait(&semaphore) == -1)
errno_abort ("Wait on semaphore"); errno_abort("Wait on semaphore");
printf ("Thread %d resuming\n", num); printf("Thread %d resuming\n", num);
return NULL; return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
int thread_count; int thread_count;
pthread_t sem_waiters[5]; pthread_t sem_waiters[5];
int status; int status;
#if !defined(_POSIX_SEMAPHORES) #if !defined(_POSIX_SEMAPHORES)
printf ("This system does not support POSIX semaphores\n"); printf("This system does not support POSIX semaphores\n");
return -1; return -1;
#else #else
if (sem_init (&semaphore, 0, 0) == -1) if (sem_init(&semaphore, 0, 0) == -1)
errno_abort ("Init semaphore"); errno_abort("Init semaphore");
/* /*
* Create 5 threads to wait concurrently on the semaphore. * Create 5 threads to wait concurrently on the semaphore.
*/ */
for (thread_count = 0; thread_count < 5; thread_count++) { for (thread_count = 0; thread_count < 5; thread_count++) {
status = pthread_create ( status = pthread_create(
&sem_waiters[thread_count], NULL, &sem_waiters[thread_count], NULL, sem_waiter, (void*) thread_count);
sem_waiter, (void*)thread_count); if (status != 0)
if (status != 0) err_abort(status, "Create thread");
err_abort (status, "Create thread"); }
}
sleep (2); sleep(2);
/* /*
* "Broadcast" the semaphore by repeatedly posting until the * "Broadcast" the semaphore by repeatedly posting until the
* count of waiters goes to 0. * count of waiters goes to 0.
*/ */
while (1) { while (1) {
int sem_value; int sem_value;
if (sem_getvalue (&semaphore, &sem_value) == -1) if (sem_getvalue(&semaphore, &sem_value) == -1)
errno_abort ("Get semaphore value"); errno_abort("Get semaphore value");
if (sem_value >= 0) if (sem_value >= 0)
break; break;
printf ("Posting from main: %d\n", sem_value); printf("Posting from main: %d\n", sem_value);
if (sem_post (&semaphore) == -1) if (sem_post(&semaphore) == -1)
errno_abort ("Post semaphore"); errno_abort("Post semaphore");
} }
/* /*
* Wait for all threads to complete. * Wait for all threads to complete.
*/ */
for (thread_count = 0; thread_count < 5; thread_count++) { for (thread_count = 0; thread_count < 5; thread_count++) {
status = pthread_join (sem_waiters[thread_count], NULL); status = pthread_join(sem_waiters[thread_count], NULL);
if (status != 0) if (status != 0)
err_abort (status, "Join thread"); err_abort(status, "Join thread");
} }
return 0; return 0;
#endif #endif
} }

View File

@@ -17,8 +17,8 @@
timer_t timer_id; timer_t timer_id;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int counter = 0; int counter = 0;
/* /*
* Thread start routine to notify the application when the * Thread start routine to notify the application when the
@@ -29,78 +29,76 @@ int counter = 0;
* be awakened, and will terminate the program. * be awakened, and will terminate the program.
*/ */
void void
timer_thread (void *arg) timer_thread(void* arg)
{ {
int status; int status;
status = pthread_mutex_lock (&mutex); status = pthread_mutex_lock(&mutex);
if (status != 0)
err_abort(status, "Lock mutex");
if (++counter >= 5) {
status = pthread_cond_signal(&cond);
if (status != 0) if (status != 0)
err_abort (status, "Lock mutex"); err_abort(status, "Signal condition");
if (++counter >= 5) { }
status = pthread_cond_signal (&cond); status = pthread_mutex_unlock(&mutex);
if (status != 0) if (status != 0)
err_abort (status, "Signal condition"); err_abort(status, "Unlock mutex");
}
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
printf ("Timer %d\n", counter); printf("Timer %d\n", counter);
} }
main() main()
{ {
int status; int status;
struct itimerspec ts; struct itimerspec ts;
struct sigevent se; struct sigevent se;
#ifdef sun #ifdef sun
fprintf ( fprintf(stderr,
stderr, "This program cannot compile on Solaris 2.5.\n"
"This program cannot compile on Solaris 2.5.\n" "To build and run on Solaris 2.6, remove the\n"
"To build and run on Solaris 2.6, remove the\n" "\"#ifdef sun\" block in main().\n");
"\"#ifdef sun\" block in main().\n");
#else #else
/* /*
* Set the sigevent structure to cause the signal to be * Set the sigevent structure to cause the signal to be
* delivered by creating a new thread. * delivered by creating a new thread.
*/ */
se.sigev_notify = SIGEV_THREAD; se.sigev_notify = SIGEV_THREAD;
se.sigev_value.sival_ptr = &timer_id; se.sigev_value.sival_ptr = &timer_id;
se.sigev_notify_function = timer_thread; se.sigev_notify_function = timer_thread;
se.sigev_notify_attributes = NULL; se.sigev_notify_attributes = NULL;
/* /*
* Specify a repeating timer that fires each 5 seconds. * Specify a repeating timer that fires each 5 seconds.
*/ */
ts.it_value.tv_sec = 5; ts.it_value.tv_sec = 5;
ts.it_value.tv_nsec = 0; ts.it_value.tv_nsec = 0;
ts.it_interval.tv_sec = 5; ts.it_interval.tv_sec = 5;
ts.it_interval.tv_nsec = 0; ts.it_interval.tv_nsec = 0;
DPRINTF (("Creating timer\n")); DPRINTF(("Creating timer\n"));
status = timer_create(CLOCK_REALTIME, &se, &timer_id); status = timer_create(CLOCK_REALTIME, &se, &timer_id);
if (status == -1) if (status == -1)
errno_abort ("Create timer"); errno_abort("Create timer");
DPRINTF (( DPRINTF(("Setting timer %d for 5-second expiration...\n", timer_id));
"Setting timer %d for 5-second expiration...\n", timer_id)); status = timer_settime(timer_id, 0, &ts, 0);
status = timer_settime(timer_id, 0, &ts, 0); if (status == -1)
if (status == -1) errno_abort("Set timer");
errno_abort ("Set timer");
status = pthread_mutex_lock (&mutex); status = pthread_mutex_lock(&mutex);
if (status != 0)
err_abort(status, "Lock mutex");
while (counter < 5) {
status = pthread_cond_wait(&cond, &mutex);
if (status != 0) if (status != 0)
err_abort (status, "Lock mutex"); err_abort(status, "Wait on condition");
while (counter < 5) { }
status = pthread_cond_wait (&cond, &mutex); status = pthread_mutex_unlock(&mutex);
if (status != 0) if (status != 0)
err_abort (status, "Wait on condition"); err_abort(status, "Unlock mutex");
}
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
#endif /* Sun */ #endif /* Sun */
return 0; return 0;
} }

View File

@@ -4,15 +4,15 @@
* Demonstrate use of sigwait() to synchronously handle * Demonstrate use of sigwait() to synchronously handle
* asynchrnous signals within a threaded program. * asynchrnous signals within a threaded program.
*/ */
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h> #include <pthread.h>
#include <signal.h> #include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "errors.h" #include "errors.h"
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int interrupted = 0; int interrupted = 0;
sigset_t signal_set; sigset_t signal_set;
/* /*
@@ -20,79 +20,80 @@ sigset_t signal_set;
* the "interrupted" flag (the main thread's wait predicate) and * the "interrupted" flag (the main thread's wait predicate) and
* signal a condition variable. The main thread will exit. * signal a condition variable. The main thread will exit.
*/ */
void *signal_waiter (void *arg) void*
signal_waiter(void* arg)
{ {
int sig_number; int sig_number;
int signal_count = 0; int signal_count = 0;
int status; int status;
while (1) { while (1) {
sigwait (&signal_set, &sig_number); sigwait(&signal_set, &sig_number);
if (sig_number == SIGINT) { if (sig_number == SIGINT) {
printf ("Got SIGINT (%d of 5)\n", signal_count+1); printf("Got SIGINT (%d of 5)\n", signal_count + 1);
if (++signal_count >= 5) { if (++signal_count >= 5) {
status = pthread_mutex_lock (&mutex); status = pthread_mutex_lock(&mutex);
if (status != 0)
err_abort (status, "Lock mutex");
interrupted = 1;
status = pthread_cond_signal (&cond);
if (status != 0)
err_abort (status, "Signal condition");
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
break;
}
}
}
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t signal_thread_id;
int status;
/*
* Start by masking the "interesting" signal, SIGINT in the
* initial thread. Because all threads inherit the signal mask
* from their creator, all threads in the process will have
* SIGINT masked unless one explicitly unmasks it. The
* semantics of sigwait require that all threads (including
* the thread calling sigwait) have the signal masked, for
* reliable operation. Otherwise, a signal that arrives
* while the sigwaiter is not blocked in sigwait might be
* delivered to another thread.
*/
sigemptyset (&signal_set);
sigaddset (&signal_set, SIGINT);
status = pthread_sigmask (SIG_BLOCK, &signal_set, NULL);
if (status != 0)
err_abort (status, "Set signal mask");
/*
* Create the sigwait thread.
*/
status = pthread_create (&signal_thread_id, NULL,
signal_waiter, NULL);
if (status != 0)
err_abort (status, "Create sigwaiter");
/*
* Wait for the sigwait thread to receive SIGINT and signal
* the condition variable.
*/
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock mutex");
while (!interrupted) {
status = pthread_cond_wait (&cond, &mutex);
if (status != 0) if (status != 0)
err_abort (status, "Wait for interrupt"); err_abort(status, "Lock mutex");
interrupted = 1;
status = pthread_cond_signal(&cond);
if (status != 0)
err_abort(status, "Signal condition");
status = pthread_mutex_unlock(&mutex);
if (status != 0)
err_abort(status, "Unlock mutex");
break;
}
} }
status = pthread_mutex_unlock (&mutex); }
if (status != 0) return NULL;
err_abort (status, "Unlock mutex"); }
printf ("Main terminating with SIGINT\n");
return 0; int
main(int argc, char* argv[])
{
pthread_t signal_thread_id;
int status;
/*
* Start by masking the "interesting" signal, SIGINT in the
* initial thread. Because all threads inherit the signal mask
* from their creator, all threads in the process will have
* SIGINT masked unless one explicitly unmasks it. The
* semantics of sigwait require that all threads (including
* the thread calling sigwait) have the signal masked, for
* reliable operation. Otherwise, a signal that arrives
* while the sigwaiter is not blocked in sigwait might be
* delivered to another thread.
*/
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGINT);
status = pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
if (status != 0)
err_abort(status, "Set signal mask");
/*
* Create the sigwait thread.
*/
status = pthread_create(&signal_thread_id, NULL, signal_waiter, NULL);
if (status != 0)
err_abort(status, "Create sigwaiter");
/*
* Wait for the sigwait thread to receive SIGINT and signal
* the condition variable.
*/
status = pthread_mutex_lock(&mutex);
if (status != 0)
err_abort(status, "Lock mutex");
while (!interrupted) {
status = pthread_cond_wait(&cond, &mutex);
if (status != 0)
err_abort(status, "Wait for interrupt");
}
status = pthread_mutex_unlock(&mutex);
if (status != 0)
err_abort(status, "Unlock mutex");
printf("Main terminating with SIGINT\n");
return 0;
} }

View File

@@ -26,15 +26,15 @@
#include <signal.h> #include <signal.h>
#include "errors.h" #include "errors.h"
#define THREAD_COUNT 20 #define THREAD_COUNT 20
#define ITERATIONS 40000 #define ITERATIONS 40000
unsigned long thread_count = THREAD_COUNT; unsigned long thread_count = THREAD_COUNT;
unsigned long iterations = ITERATIONS; unsigned long iterations = ITERATIONS;
pthread_mutex_t the_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t the_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
volatile int sentinel = 0; volatile int sentinel = 0;
pthread_once_t once = PTHREAD_ONCE_INIT; pthread_once_t once = PTHREAD_ONCE_INIT;
pthread_t *array = NULL, null_pthread = {0}; pthread_t *array = NULL, null_pthread = {0};
int bottom = 0; int bottom = 0;
int inited = 0; int inited = 0;
@@ -44,23 +44,23 @@ int inited = 0;
* receiving SIGUSR2 (resume). * receiving SIGUSR2 (resume).
*/ */
void void
suspend_signal_handler (int sig) suspend_signal_handler(int sig)
{ {
sigset_t signal_set; sigset_t signal_set;
/* /*
* Block all signals except SIGUSR2 while suspended. * Block all signals except SIGUSR2 while suspended.
*/ */
sigfillset (&signal_set); sigfillset(&signal_set);
sigdelset (&signal_set, SIGUSR2); sigdelset(&signal_set, SIGUSR2);
sentinel = 1; sentinel = 1;
sigsuspend (&signal_set); sigsuspend(&signal_set);
/* /*
* Once I'm here, I've been resumed, and the resume signal * Once I'm here, I've been resumed, and the resume signal
* handler has been run to completion. * handler has been run to completion.
*/ */
return; return;
} }
/* /*
@@ -69,9 +69,9 @@ suspend_signal_handler (int sig)
* to cause sigsuspend() to return. * to cause sigsuspend() to return.
*/ */
void void
resume_signal_handler (int sig) resume_signal_handler(int sig)
{ {
return; return;
} }
/* /*
@@ -79,39 +79,39 @@ resume_signal_handler (int sig)
* (called by pthread_once). * (called by pthread_once).
*/ */
void void
suspend_init_routine (void) suspend_init_routine(void)
{ {
int status; int status;
struct sigaction sigusr1, sigusr2; struct sigaction sigusr1, sigusr2;
/* /*
* Allocate the suspended threads array. This array is used * Allocate the suspended threads array. This array is used
* to guarentee idempotency * to guarentee idempotency
*/ */
bottom = 10; bottom = 10;
array = (pthread_t*) calloc (bottom, sizeof (pthread_t)); array = (pthread_t*) calloc(bottom, sizeof(pthread_t));
/* /*
* Install the signal handlers for suspend/resume. * Install the signal handlers for suspend/resume.
*/ */
sigusr1.sa_flags = 0; sigusr1.sa_flags = 0;
sigusr1.sa_handler = suspend_signal_handler; sigusr1.sa_handler = suspend_signal_handler;
sigemptyset (&sigusr1.sa_mask); sigemptyset(&sigusr1.sa_mask);
sigusr2.sa_flags = 0; sigusr2.sa_flags = 0;
sigusr2.sa_handler = resume_signal_handler; sigusr2.sa_handler = resume_signal_handler;
sigusr2.sa_mask = sigusr1.sa_mask; sigusr2.sa_mask = sigusr1.sa_mask;
status = sigaction (SIGUSR1, &sigusr1, NULL); status = sigaction(SIGUSR1, &sigusr1, NULL);
if (status == -1) if (status == -1)
errno_abort ("Installing suspend handler"); errno_abort("Installing suspend handler");
status = sigaction (SIGUSR2, &sigusr2, NULL); status = sigaction(SIGUSR2, &sigusr2, NULL);
if (status == -1) if (status == -1)
errno_abort ("Installing resume handler"); errno_abort("Installing resume handler");
inited = 1; inited = 1;
return; return;
} }
/* /*
@@ -122,79 +122,76 @@ suspend_init_routine (void)
* additional effect on the thread -- a single thd_continue * additional effect on the thread -- a single thd_continue
* call will cause it to resume execution. * call will cause it to resume execution.
*/ */
int int
thd_suspend (pthread_t target_thread) thd_suspend(pthread_t target_thread)
{ {
int status; int status;
int i = 0; int i = 0;
/* /*
* The first call to thd_suspend will initialize the * The first call to thd_suspend will initialize the
* package. * package.
*/ */
status = pthread_once (&once, suspend_init_routine); status = pthread_once(&once, suspend_init_routine);
if (status != 0) if (status != 0)
return status;
/*
* Serialize access to suspend, makes life easier
*/
status = pthread_mutex_lock (&mut);
if (status != 0)
return status;
/*
* Threads that are suspended are added to the target_array;
* a request to suspend a thread already listed in the array
* is ignored. Sending a second SIGUSR1 would cause the
* thread to re-suspend itself as soon as it is resumed.
*/
while (i < bottom)
if (array[i++] == target_thread) {
status = pthread_mutex_unlock (&mut);
return status;
}
/*
* Ok, we really need to suspend this thread. So, lets find
* the location in the array that we'll use. If we run off
* the end, realloc the array for more space.
*/
i = 0;
while (array[i] != 0)
i++;
if (i == bottom) {
array = (pthread_t*) realloc (
array, (++bottom * sizeof (pthread_t)));
if (array == NULL) {
pthread_mutex_unlock (&mut);
return errno;
}
array[bottom] = null_pthread; /* Clear new entry */
}
/*
* Clear the sentinel and signal the thread to suspend.
*/
sentinel = 0;
status = pthread_kill (target_thread, SIGUSR1);
if (status != 0) {
pthread_mutex_unlock (&mut);
return status;
}
/*
* Wait for the sentinel to change.
*/
while (sentinel == 0)
sched_yield ();
array[i] = target_thread;
status = pthread_mutex_unlock (&mut);
return status; return status;
/*
* Serialize access to suspend, makes life easier
*/
status = pthread_mutex_lock(&mut);
if (status != 0)
return status;
/*
* Threads that are suspended are added to the target_array;
* a request to suspend a thread already listed in the array
* is ignored. Sending a second SIGUSR1 would cause the
* thread to re-suspend itself as soon as it is resumed.
*/
while (i < bottom)
if (array[i++] == target_thread) {
status = pthread_mutex_unlock(&mut);
return status;
}
/*
* Ok, we really need to suspend this thread. So, lets find
* the location in the array that we'll use. If we run off
* the end, realloc the array for more space.
*/
i = 0;
while (array[i] != 0) i++;
if (i == bottom) {
array = (pthread_t*) realloc(array, (++bottom * sizeof(pthread_t)));
if (array == NULL) {
pthread_mutex_unlock(&mut);
return errno;
}
array[bottom] = null_pthread; /* Clear new entry */
}
/*
* Clear the sentinel and signal the thread to suspend.
*/
sentinel = 0;
status = pthread_kill(target_thread, SIGUSR1);
if (status != 0) {
pthread_mutex_unlock(&mut);
return status;
}
/*
* Wait for the sentinel to change.
*/
while (sentinel == 0) sched_yield();
array[i] = target_thread;
status = pthread_mutex_unlock(&mut);
return status;
} }
/* /*
@@ -202,141 +199,136 @@ thd_suspend (pthread_t target_thread)
* it out of the sigsuspend() in which it's waiting. If the * it out of the sigsuspend() in which it's waiting. If the
* target thread isn't suspended, return with success. * target thread isn't suspended, return with success.
*/ */
int int
thd_continue (pthread_t target_thread) thd_continue(pthread_t target_thread)
{ {
int status; int status;
int i = 0; int i = 0;
/* /*
* Serialize access to suspend, makes life easier * Serialize access to suspend, makes life easier
*/ */
status = pthread_mutex_lock (&mut); status = pthread_mutex_lock(&mut);
if (status != 0) if (status != 0)
return status;
/*
* If we haven't been initialized, then the thread must be "resumed"
* it couldn't have been suspended!
*/
if (!inited) {
status = pthread_mutex_unlock (&mut);
return status;
}
/*
* Make sure the thread is in the suspend array. If not, it
* hasn't been suspended (or it has already been resumed) and
* we can just carry on.
*/
while (array[i] != target_thread && i < bottom)
i++;
if (i >= bottom) {
pthread_mutex_unlock (&mut);
return 0;
}
/*
* Signal the thread to continue, and remove the thread from
* the suspended array.
*/
status = pthread_kill (target_thread, SIGUSR2);
if (status != 0) {
pthread_mutex_unlock (&mut);
return status;
}
array[i] = 0; /* Clear array element */
status = pthread_mutex_unlock (&mut);
return status; return status;
/*
* If we haven't been initialized, then the thread must be "resumed"
* it couldn't have been suspended!
*/
if (!inited) {
status = pthread_mutex_unlock(&mut);
return status;
}
/*
* Make sure the thread is in the suspend array. If not, it
* hasn't been suspended (or it has already been resumed) and
* we can just carry on.
*/
while (array[i] != target_thread && i < bottom) i++;
if (i >= bottom) {
pthread_mutex_unlock(&mut);
return 0;
}
/*
* Signal the thread to continue, and remove the thread from
* the suspended array.
*/
status = pthread_kill(target_thread, SIGUSR2);
if (status != 0) {
pthread_mutex_unlock(&mut);
return status;
}
array[i] = 0; /* Clear array element */
status = pthread_mutex_unlock(&mut);
return status;
} }
static void * static void*
thread_routine (void *arg) thread_routine(void* arg)
{ {
int number = (int)arg; int number = (int) arg;
int status; int status;
int i; int i;
char buffer[128]; char buffer[128];
for (i = 1; i <= iterations; i++) { for (i = 1; i <= iterations; i++) {
/* /*
* Every time each thread does 5000 interations, print * Every time each thread does 5000 interations, print
* a progress report. * a progress report.
*/ */
if (i % 2000 == 0) { if (i % 2000 == 0) {
sprintf ( sprintf(buffer, "Thread %02d: %d\n", number, i);
buffer, "Thread %02d: %d\n", write(1, buffer, strlen(buffer));
number, i);
write (1, buffer, strlen (buffer));
}
sched_yield ();
} }
return (void *)0; sched_yield();
}
return (void*) 0;
} }
int int
main (int argc, char *argv[]) main(int argc, char* argv[])
{ {
pthread_t threads[THREAD_COUNT]; pthread_t threads[THREAD_COUNT];
pthread_attr_t detach; pthread_attr_t detach;
int status; int status;
void *result; void* result;
int i; int i;
status = pthread_attr_init (&detach); status = pthread_attr_init(&detach);
if (status != 0)
err_abort(status, "Init attributes object");
status = pthread_attr_setdetachstate(&detach, PTHREAD_CREATE_DETACHED);
if (status != 0)
err_abort(status, "Set create-detached");
for (i = 0; i < THREAD_COUNT; i++) {
status = pthread_create(&threads[i], &detach, thread_routine, (void*) i);
if (status != 0) if (status != 0)
err_abort (status, "Init attributes object"); err_abort(status, "Create thread");
status = pthread_attr_setdetachstate ( }
&detach, PTHREAD_CREATE_DETACHED);
sleep(2);
for (i = 0; i < THREAD_COUNT / 2; i++) {
printf("Suspending thread %d.\n", i);
status = thd_suspend(threads[i]);
if (status != 0) if (status != 0)
err_abort (status, "Set create-detached"); err_abort(status, "Suspend thread");
}
for (i = 0; i< THREAD_COUNT; i++) { printf("Sleeping ...\n");
status = pthread_create ( sleep(2);
&threads[i], &detach, thread_routine, (void *)i);
if (status != 0)
err_abort (status, "Create thread");
}
sleep (2); for (i = 0; i < THREAD_COUNT / 2; i++) {
printf("Continuing thread %d.\n", i);
status = thd_continue(threads[i]);
if (status != 0)
err_abort(status, "Suspend thread");
}
for (i = 0; i < THREAD_COUNT/2; i++) { for (i = THREAD_COUNT / 2; i < THREAD_COUNT; i++) {
printf ("Suspending thread %d.\n", i); printf("Suspending thread %d.\n", i);
status = thd_suspend (threads[i]); status = thd_suspend(threads[i]);
if (status != 0) if (status != 0)
err_abort (status, "Suspend thread"); err_abort(status, "Suspend thread");
} }
printf ("Sleeping ...\n"); printf("Sleeping ...\n");
sleep (2); sleep(2);
for (i = 0; i < THREAD_COUNT/2; i++) { for (i = THREAD_COUNT / 2; i < THREAD_COUNT; i++) {
printf ("Continuing thread %d.\n", i); printf("Continuing thread %d.\n", i);
status = thd_continue (threads[i]); status = thd_continue(threads[i]);
if (status != 0) if (status != 0)
err_abort (status, "Suspend thread"); err_abort(status, "Continue thread");
} }
for (i = THREAD_COUNT/2; i < THREAD_COUNT; i++) { pthread_exit(NULL); /* Let threads finish */
printf ("Suspending thread %d.\n", i);
status = thd_suspend (threads[i]);
if (status != 0)
err_abort (status, "Suspend thread");
}
printf ("Sleeping ...\n");
sleep (2);
for (i = THREAD_COUNT/2; i < THREAD_COUNT; i++) {
printf ("Continuing thread %d.\n", i);
status = thd_continue (threads[i]);
if (status != 0)
err_abort (status, "Continue thread");
}
pthread_exit (NULL); /* Let threads finish */
} }

View File

@@ -20,66 +20,68 @@
* to do something in a serial region before entering another * to do something in a serial region before entering another
* parallel section of code. * parallel section of code.
*/ */
#include "barrier.h"
#include <pthread.h> #include <pthread.h>
#include "errors.h" #include "errors.h"
#include "barrier.h"
/* /*
* Initialize a barrier for use. * Initialize a barrier for use.
*/ */
int barrier_init (barrier_t *barrier, int count) int
barrier_init(barrier_t* barrier, int count)
{ {
int status; int status;
barrier->threshold = barrier->counter = count; barrier->threshold = barrier->counter = count;
barrier->cycle = 0; barrier->cycle = 0;
status = pthread_mutex_init (&barrier->mutex, NULL); status = pthread_mutex_init(&barrier->mutex, NULL);
if (status != 0) if (status != 0)
return status; return status;
status = pthread_cond_init (&barrier->cv, NULL); status = pthread_cond_init(&barrier->cv, NULL);
if (status != 0) { if (status != 0) {
pthread_mutex_destroy (&barrier->mutex); pthread_mutex_destroy(&barrier->mutex);
return status; return status;
} }
barrier->valid = BARRIER_VALID; barrier->valid = BARRIER_VALID;
return 0; return 0;
} }
/* /*
* Destroy a barrier when done using it. * Destroy a barrier when done using it.
*/ */
int barrier_destroy (barrier_t *barrier) int
barrier_destroy(barrier_t* barrier)
{ {
int status, status2; int status, status2;
if (barrier->valid != BARRIER_VALID) if (barrier->valid != BARRIER_VALID)
return EINVAL; return EINVAL;
status = pthread_mutex_lock (&barrier->mutex); status = pthread_mutex_lock(&barrier->mutex);
if (status != 0) if (status != 0)
return status; return status;
/* /*
* Check whether any threads are known to be waiting; report * Check whether any threads are known to be waiting; report
* "BUSY" if so. * "BUSY" if so.
*/ */
if (barrier->counter != barrier->threshold) { if (barrier->counter != barrier->threshold) {
pthread_mutex_unlock (&barrier->mutex); pthread_mutex_unlock(&barrier->mutex);
return EBUSY; return EBUSY;
} }
barrier->valid = 0; barrier->valid = 0;
status = pthread_mutex_unlock (&barrier->mutex); status = pthread_mutex_unlock(&barrier->mutex);
if (status != 0) if (status != 0)
return status; return status;
/* /*
* If unable to destroy either 1003.1c synchronization * If unable to destroy either 1003.1c synchronization
* object, return the error status. * object, return the error status.
*/ */
status = pthread_mutex_destroy (&barrier->mutex); status = pthread_mutex_destroy(&barrier->mutex);
status2 = pthread_cond_destroy (&barrier->cv); status2 = pthread_cond_destroy(&barrier->cv);
return (status == 0 ? status : status2); return (status == 0 ? status : status2);
} }
/* /*
@@ -87,58 +89,60 @@ int barrier_destroy (barrier_t *barrier)
* the count (of remaining members) reaches 0, broadcast to wake * the count (of remaining members) reaches 0, broadcast to wake
* all threads waiting. * all threads waiting.
*/ */
int barrier_wait (barrier_t *barrier) int
barrier_wait(barrier_t* barrier)
{ {
int status, cancel, tmp, cycle; int status, cancel, tmp, cycle;
if (barrier->valid != BARRIER_VALID) if (barrier->valid != BARRIER_VALID)
return EINVAL; return EINVAL;
status = pthread_mutex_lock (&barrier->mutex); status = pthread_mutex_lock(&barrier->mutex);
if (status != 0) if (status != 0)
return status; return status;
cycle = barrier->cycle; /* Remember which cycle we're on */ cycle = barrier->cycle; /* Remember which cycle we're on */
if (--barrier->counter == 0) { if (--barrier->counter == 0) {
barrier->cycle = !barrier->cycle; barrier->cycle = !barrier->cycle;
barrier->counter = barrier->threshold; barrier->counter = barrier->threshold;
status = pthread_cond_broadcast (&barrier->cv); status = pthread_cond_broadcast(&barrier->cv);
/*
* The last thread into the barrier will return status
* -1 rather than 0, so that it can be used to perform
* some special serial code following the barrier.
*/
if (status == 0)
status = -1;
} else {
/*
* Wait with cancellation disabled, because barrier_wait
* should not be a cancellation point.
*/
pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &cancel);
/*
* Wait until the barrier's cycle changes, which means
* that it has been broadcast, and we don't want to wait
* anymore.
*/
while (cycle == barrier->cycle) {
status = pthread_cond_wait (
&barrier->cv, &barrier->mutex);
if (status != 0) break;
}
pthread_setcancelstate (cancel, &tmp);
}
/* /*
* Ignore an error in unlocking. It shouldn't happen, and * The last thread into the barrier will return status
* reporting it here would be misleading -- the barrier wait * -1 rather than 0, so that it can be used to perform
* completed, after all, whereas returning, for example, * some special serial code following the barrier.
* EINVAL would imply the wait had failed. The next attempt
* to use the barrier *will* return an error, or hang, due
* to whatever happened to the mutex.
*/ */
pthread_mutex_unlock (&barrier->mutex); if (status == 0)
return status; /* error, -1 for waker, or 0 */ status = -1;
}
else {
/*
* Wait with cancellation disabled, because barrier_wait
* should not be a cancellation point.
*/
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel);
/*
* Wait until the barrier's cycle changes, which means
* that it has been broadcast, and we don't want to wait
* anymore.
*/
while (cycle == barrier->cycle) {
status = pthread_cond_wait(&barrier->cv, &barrier->mutex);
if (status != 0)
break;
}
pthread_setcancelstate(cancel, &tmp);
}
/*
* Ignore an error in unlocking. It shouldn't happen, and
* reporting it here would be misleading -- the barrier wait
* completed, after all, whereas returning, for example,
* EINVAL would imply the wait had failed. The next attempt
* to use the barrier *will* return an error, or hang, due
* to whatever happened to the mutex.
*/
pthread_mutex_unlock(&barrier->mutex);
return status; /* error, -1 for waker, or 0 */
} }

View File

@@ -17,26 +17,28 @@
* Structure describing a barrier. * Structure describing a barrier.
*/ */
typedef struct barrier_tag { typedef struct barrier_tag {
pthread_mutex_t mutex; /* Control access to barrier */ pthread_mutex_t mutex; /* Control access to barrier */
pthread_cond_t cv; /* wait for barrier */ pthread_cond_t cv; /* wait for barrier */
int valid; /* set when valid */ int valid; /* set when valid */
int threshold; /* number of threads required */ int threshold; /* number of threads required */
int counter; /* current number of threads */ int counter; /* current number of threads */
int cycle; /* alternate wait cycles (0 or 1) */ int cycle; /* alternate wait cycles (0 or 1) */
} barrier_t; } barrier_t;
#define BARRIER_VALID 0xdbcafe #define BARRIER_VALID 0xdbcafe
/* /*
* Support static initialization of barriers * Support static initialization of barriers
*/ */
#define BARRIER_INITIALIZER(cnt) \ #define BARRIER_INITIALIZER(cnt) \
{PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, \ { \
BARRIER_VALID, cnt, cnt, 0} PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, BARRIER_VALID, cnt, \
cnt, 0 \
}
/* /*
* Define barrier functions * Define barrier functions
*/ */
extern int barrier_init (barrier_t *barrier, int count); extern int barrier_init(barrier_t* barrier, int count);
extern int barrier_destroy (barrier_t *barrier); extern int barrier_destroy(barrier_t* barrier);
extern int barrier_wait (barrier_t *barrier); extern int barrier_wait(barrier_t* barrier);

View File

@@ -17,11 +17,11 @@
* Keep track of each thread * Keep track of each thread
*/ */
typedef struct thread_tag { typedef struct thread_tag {
pthread_t thread_id; pthread_t thread_id;
int number; int number;
int increment; int increment;
int array[ARRAY]; int array[ARRAY];
} thread_t; } thread_t;
barrier_t barrier; barrier_t barrier;
thread_t thread[THREADS]; thread_t thread[THREADS];
@@ -29,87 +29,91 @@ thread_t thread[THREADS];
/* /*
* Start routine for threads. * Start routine for threads.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
thread_t *self = (thread_t*)arg; /* Thread's thread_t */ thread_t* self = (thread_t*) arg; /* Thread's thread_t */
int in_loop, out_loop, count, status; int in_loop, out_loop, count, status;
/*
* Loop through OUTLOOPS barrier cycles.
*/
for (out_loop = 0; out_loop < OUTLOOPS; out_loop++) {
status = barrier_wait(&barrier);
if (status > 0)
err_abort(status, "Wait on barrier");
/* /*
* Loop through OUTLOOPS barrier cycles. * This inner loop just adds a value to each element in
* the working array.
*/ */
for (out_loop = 0; out_loop < OUTLOOPS; out_loop++) { for (in_loop = 0; in_loop < INLOOPS; in_loop++)
status = barrier_wait (&barrier); for (count = 0; count < ARRAY; count++)
if (status > 0) self->array[count] += self->increment;
err_abort (status, "Wait on barrier");
/* status = barrier_wait(&barrier);
* This inner loop just adds a value to each element in if (status > 0)
* the working array. err_abort(status, "Wait on barrier");
*/
for (in_loop = 0; in_loop < INLOOPS; in_loop++)
for (count = 0; count < ARRAY; count++)
self->array[count] += self->increment;
status = barrier_wait (&barrier); /*
if (status > 0) * The barrier causes one thread to return with the
err_abort (status, "Wait on barrier"); * special return status -1. The thread receiving this
* value increments each element in the shared array.
*/
if (status == -1) {
int thread_num;
/* for (thread_num = 0; thread_num < THREADS; thread_num++)
* The barrier causes one thread to return with the thread[thread_num].increment += 1;
* special return status -1. The thread receiving this
* value increments each element in the shared array.
*/
if (status == -1) {
int thread_num;
for (thread_num = 0; thread_num < THREADS; thread_num++)
thread[thread_num].increment += 1;
}
} }
return NULL; }
return NULL;
} }
int main (int arg, char *argv[]) int
main(int arg, char* argv[])
{ {
int thread_count, array_count; int thread_count, array_count;
int status; int status;
barrier_init (&barrier, THREADS); barrier_init(&barrier, THREADS);
/* /*
* Create a set of threads that will use the barrier. * Create a set of threads that will use the barrier.
*/ */
for (thread_count = 0; thread_count < THREADS; thread_count++) { for (thread_count = 0; thread_count < THREADS; thread_count++) {
thread[thread_count].increment = thread_count; thread[thread_count].increment = thread_count;
thread[thread_count].number = thread_count; thread[thread_count].number = thread_count;
for (array_count = 0; array_count < ARRAY; array_count++) for (array_count = 0; array_count < ARRAY; array_count++)
thread[thread_count].array[array_count] = array_count + 1; thread[thread_count].array[array_count] = array_count + 1;
status = pthread_create (&thread[thread_count].thread_id, status = pthread_create(&thread[thread_count].thread_id,
NULL, thread_routine, (void*)&thread[thread_count]); NULL,
if (status != 0) thread_routine,
err_abort (status, "Create thread"); (void*) &thread[thread_count]);
} if (status != 0)
err_abort(status, "Create thread");
}
/* /*
* Now join with each of the threads. * Now join with each of the threads.
*/ */
for (thread_count = 0; thread_count < THREADS; thread_count++) { for (thread_count = 0; thread_count < THREADS; thread_count++) {
status = pthread_join (thread[thread_count].thread_id, NULL); status = pthread_join(thread[thread_count].thread_id, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Join thread"); err_abort(status, "Join thread");
printf ("%02d: (%d) ", thread_count, thread[thread_count].increment); printf("%02d: (%d) ", thread_count, thread[thread_count].increment);
for (array_count = 0; array_count < ARRAY; array_count++) for (array_count = 0; array_count < ARRAY; array_count++)
printf ("%010u ", thread[thread_count].array[array_count]); printf("%010u ", thread[thread_count].array[array_count]);
printf ("\n"); printf("\n");
} }
/* /*
* To be thorough, destroy the barrier. * To be thorough, destroy the barrier.
*/ */
barrier_destroy (&barrier); barrier_destroy(&barrier);
return 0; return 0;
} }

View File

@@ -21,79 +21,81 @@
* lock. rwl_writetrylock() attempts to lock a read-write lock * lock. rwl_writetrylock() attempts to lock a read-write lock
* for write access, and returns EBUSY instead of blocking. * for write access, and returns EBUSY instead of blocking.
*/ */
#include "rwlock.h"
#include <pthread.h> #include <pthread.h>
#include "errors.h" #include "errors.h"
#include "rwlock.h"
/* /*
* Initialize a read-write lock * Initialize a read-write lock
*/ */
int rwl_init (rwlock_t *rwl) int
rwl_init(rwlock_t* rwl)
{ {
int status; int status;
rwl->r_active = 0; rwl->r_active = 0;
rwl->r_wait = rwl->w_wait = 0; rwl->r_wait = rwl->w_wait = 0;
rwl->w_active = 0; rwl->w_active = 0;
status = pthread_mutex_init (&rwl->mutex, NULL); status = pthread_mutex_init(&rwl->mutex, NULL);
if (status != 0) if (status != 0)
return status; return status;
status = pthread_cond_init (&rwl->read, NULL); status = pthread_cond_init(&rwl->read, NULL);
if (status != 0) { if (status != 0) {
/* if unable to create read CV, destroy mutex */ /* if unable to create read CV, destroy mutex */
pthread_mutex_destroy (&rwl->mutex); pthread_mutex_destroy(&rwl->mutex);
return status; return status;
} }
status = pthread_cond_init (&rwl->write, NULL); status = pthread_cond_init(&rwl->write, NULL);
if (status != 0) { if (status != 0) {
/* if unable to create write CV, destroy read CV and mutex */ /* if unable to create write CV, destroy read CV and mutex */
pthread_cond_destroy (&rwl->read); pthread_cond_destroy(&rwl->read);
pthread_mutex_destroy (&rwl->mutex); pthread_mutex_destroy(&rwl->mutex);
return status; return status;
} }
rwl->valid = RWLOCK_VALID; rwl->valid = RWLOCK_VALID;
return 0; return 0;
} }
/* /*
* Destroy a read-write lock * Destroy a read-write lock
*/ */
int rwl_destroy (rwlock_t *rwl) int
rwl_destroy(rwlock_t* rwl)
{ {
int status, status1, status2; int status, status1, status2;
if (rwl->valid != RWLOCK_VALID) if (rwl->valid != RWLOCK_VALID)
return EINVAL; return EINVAL;
status = pthread_mutex_lock (&rwl->mutex); status = pthread_mutex_lock(&rwl->mutex);
if (status != 0) if (status != 0)
return status; return status;
/* /*
* Check whether any threads own the lock; report "BUSY" if * Check whether any threads own the lock; report "BUSY" if
* so. * so.
*/ */
if (rwl->r_active > 0 || rwl->w_active) { if (rwl->r_active > 0 || rwl->w_active) {
pthread_mutex_unlock (&rwl->mutex); pthread_mutex_unlock(&rwl->mutex);
return EBUSY; return EBUSY;
} }
/* /*
* Check whether any threads are known to be waiting; report * Check whether any threads are known to be waiting; report
* EBUSY if so. * EBUSY if so.
*/ */
if (rwl->r_wait != 0 || rwl->w_wait != 0) { if (rwl->r_wait != 0 || rwl->w_wait != 0) {
pthread_mutex_unlock (&rwl->mutex); pthread_mutex_unlock(&rwl->mutex);
return EBUSY; return EBUSY;
} }
rwl->valid = 0; rwl->valid = 0;
status = pthread_mutex_unlock (&rwl->mutex); status = pthread_mutex_unlock(&rwl->mutex);
if (status != 0) if (status != 0)
return status; return status;
status = pthread_mutex_destroy (&rwl->mutex); status = pthread_mutex_destroy(&rwl->mutex);
status1 = pthread_cond_destroy (&rwl->read); status1 = pthread_cond_destroy(&rwl->read);
status2 = pthread_cond_destroy (&rwl->write); status2 = pthread_cond_destroy(&rwl->write);
return (status == 0 ? status : (status1 == 0 ? status1 : status2)); return (status == 0 ? status : (status1 == 0 ? status1 : status2));
} }
/* /*
@@ -103,81 +105,85 @@ int rwl_destroy (rwlock_t *rwl)
* Simply record that the thread is no longer waiting, * Simply record that the thread is no longer waiting,
* and unlock the mutex. * and unlock the mutex.
*/ */
static void rwl_readcleanup (void *arg) static void
rwl_readcleanup(void* arg)
{ {
rwlock_t *rwl = (rwlock_t *)arg; rwlock_t* rwl = (rwlock_t*) arg;
rwl->r_wait--; rwl->r_wait--;
pthread_mutex_unlock (&rwl->mutex); pthread_mutex_unlock(&rwl->mutex);
} }
/* /*
* Lock a read-write lock for read access. * Lock a read-write lock for read access.
*/ */
int rwl_readlock (rwlock_t *rwl) int
rwl_readlock(rwlock_t* rwl)
{ {
int status; int status;
if (rwl->valid != RWLOCK_VALID) if (rwl->valid != RWLOCK_VALID)
return EINVAL; return EINVAL;
status = pthread_mutex_lock (&rwl->mutex); status = pthread_mutex_lock(&rwl->mutex);
if (status != 0) if (status != 0)
return status;
if (rwl->w_active) {
rwl->r_wait++;
pthread_cleanup_push (rwl_readcleanup, (void*)rwl);
while (rwl->w_active) {
status = pthread_cond_wait (&rwl->read, &rwl->mutex);
if (status != 0)
break;
}
pthread_cleanup_pop (0);
rwl->r_wait--;
}
if (status == 0)
rwl->r_active++;
pthread_mutex_unlock (&rwl->mutex);
return status; return status;
if (rwl->w_active) {
rwl->r_wait++;
pthread_cleanup_push(rwl_readcleanup, (void*) rwl);
while (rwl->w_active) {
status = pthread_cond_wait(&rwl->read, &rwl->mutex);
if (status != 0)
break;
}
pthread_cleanup_pop(0);
rwl->r_wait--;
}
if (status == 0)
rwl->r_active++;
pthread_mutex_unlock(&rwl->mutex);
return status;
} }
/* /*
* Attempt to lock a read-write lock for read access (don't * Attempt to lock a read-write lock for read access (don't
* block if unavailable). * block if unavailable).
*/ */
int rwl_readtrylock (rwlock_t *rwl) int
rwl_readtrylock(rwlock_t* rwl)
{ {
int status, status2; int status, status2;
if (rwl->valid != RWLOCK_VALID) if (rwl->valid != RWLOCK_VALID)
return EINVAL; return EINVAL;
status = pthread_mutex_lock (&rwl->mutex); status = pthread_mutex_lock(&rwl->mutex);
if (status != 0) if (status != 0)
return status; return status;
if (rwl->w_active) if (rwl->w_active)
status = EBUSY; status = EBUSY;
else else
rwl->r_active++; rwl->r_active++;
status2 = pthread_mutex_unlock (&rwl->mutex); status2 = pthread_mutex_unlock(&rwl->mutex);
return (status2 != 0 ? status2 : status); return (status2 != 0 ? status2 : status);
} }
/* /*
* Unlock a read-write lock from read access. * Unlock a read-write lock from read access.
*/ */
int rwl_readunlock (rwlock_t *rwl) int
rwl_readunlock(rwlock_t* rwl)
{ {
int status, status2; int status, status2;
if (rwl->valid != RWLOCK_VALID) if (rwl->valid != RWLOCK_VALID)
return EINVAL; return EINVAL;
status = pthread_mutex_lock (&rwl->mutex); status = pthread_mutex_lock(&rwl->mutex);
if (status != 0) if (status != 0)
return status; return status;
rwl->r_active--; rwl->r_active--;
if (rwl->r_active == 0 && rwl->w_wait > 0) if (rwl->r_active == 0 && rwl->w_wait > 0)
status = pthread_cond_signal (&rwl->write); status = pthread_cond_signal(&rwl->write);
status2 = pthread_mutex_unlock (&rwl->mutex); status2 = pthread_mutex_unlock(&rwl->mutex);
return (status2 == 0 ? status : status2); return (status2 == 0 ? status : status2);
} }
/* /*
@@ -187,90 +193,95 @@ int rwl_readunlock (rwlock_t *rwl)
* Simply record that the thread is no longer waiting, * Simply record that the thread is no longer waiting,
* and unlock the mutex. * and unlock the mutex.
*/ */
static void rwl_writecleanup (void *arg) static void
rwl_writecleanup(void* arg)
{ {
rwlock_t *rwl = (rwlock_t *)arg; rwlock_t* rwl = (rwlock_t*) arg;
rwl->w_wait--; rwl->w_wait--;
pthread_mutex_unlock (&rwl->mutex); pthread_mutex_unlock(&rwl->mutex);
} }
/* /*
* Lock a read-write lock for write access. * Lock a read-write lock for write access.
*/ */
int rwl_writelock (rwlock_t *rwl) int
rwl_writelock(rwlock_t* rwl)
{ {
int status; int status;
if (rwl->valid != RWLOCK_VALID) if (rwl->valid != RWLOCK_VALID)
return EINVAL; return EINVAL;
status = pthread_mutex_lock (&rwl->mutex); status = pthread_mutex_lock(&rwl->mutex);
if (status != 0) if (status != 0)
return status;
if (rwl->w_active || rwl->r_active > 0) {
rwl->w_wait++;
pthread_cleanup_push (rwl_writecleanup, (void*)rwl);
while (rwl->w_active || rwl->r_active > 0) {
status = pthread_cond_wait (&rwl->write, &rwl->mutex);
if (status != 0)
break;
}
pthread_cleanup_pop (0);
rwl->w_wait--;
}
if (status == 0)
rwl->w_active = 1;
pthread_mutex_unlock (&rwl->mutex);
return status; return status;
if (rwl->w_active || rwl->r_active > 0) {
rwl->w_wait++;
pthread_cleanup_push(rwl_writecleanup, (void*) rwl);
while (rwl->w_active || rwl->r_active > 0) {
status = pthread_cond_wait(&rwl->write, &rwl->mutex);
if (status != 0)
break;
}
pthread_cleanup_pop(0);
rwl->w_wait--;
}
if (status == 0)
rwl->w_active = 1;
pthread_mutex_unlock(&rwl->mutex);
return status;
} }
/* /*
* Attempt to lock a read-write lock for write access. Don't * Attempt to lock a read-write lock for write access. Don't
* block if unavailable. * block if unavailable.
*/ */
int rwl_writetrylock (rwlock_t *rwl) int
rwl_writetrylock(rwlock_t* rwl)
{ {
int status, status2; int status, status2;
if (rwl->valid != RWLOCK_VALID) if (rwl->valid != RWLOCK_VALID)
return EINVAL; return EINVAL;
status = pthread_mutex_lock (&rwl->mutex); status = pthread_mutex_lock(&rwl->mutex);
if (status != 0) if (status != 0)
return status; return status;
if (rwl->w_active || rwl->r_active > 0) if (rwl->w_active || rwl->r_active > 0)
status = EBUSY; status = EBUSY;
else else
rwl->w_active = 1; rwl->w_active = 1;
status2 = pthread_mutex_unlock (&rwl->mutex); status2 = pthread_mutex_unlock(&rwl->mutex);
return (status != 0 ? status : status2); return (status != 0 ? status : status2);
} }
/* /*
* Unlock a read-write lock from write access. * Unlock a read-write lock from write access.
*/ */
int rwl_writeunlock (rwlock_t *rwl) int
rwl_writeunlock(rwlock_t* rwl)
{ {
int status; int status;
if (rwl->valid != RWLOCK_VALID) if (rwl->valid != RWLOCK_VALID)
return EINVAL; return EINVAL;
status = pthread_mutex_lock (&rwl->mutex); status = pthread_mutex_lock(&rwl->mutex);
if (status != 0) if (status != 0)
return status;
rwl->w_active = 0;
if (rwl->r_wait > 0) {
status = pthread_cond_broadcast (&rwl->read);
if (status != 0) {
pthread_mutex_unlock (&rwl->mutex);
return status;
}
} else if (rwl->w_wait > 0) {
status = pthread_cond_signal (&rwl->write);
if (status != 0) {
pthread_mutex_unlock (&rwl->mutex);
return status;
}
}
status = pthread_mutex_unlock (&rwl->mutex);
return status; return status;
rwl->w_active = 0;
if (rwl->r_wait > 0) {
status = pthread_cond_broadcast(&rwl->read);
if (status != 0) {
pthread_mutex_unlock(&rwl->mutex);
return status;
}
}
else if (rwl->w_wait > 0) {
status = pthread_cond_signal(&rwl->write);
if (status != 0) {
pthread_mutex_unlock(&rwl->mutex);
return status;
}
}
status = pthread_mutex_unlock(&rwl->mutex);
return status;
} }

View File

@@ -17,33 +17,35 @@
* Structure describing a read-write lock. * Structure describing a read-write lock.
*/ */
typedef struct rwlock_tag { typedef struct rwlock_tag {
pthread_mutex_t mutex; pthread_mutex_t mutex;
pthread_cond_t read; /* wait for read */ pthread_cond_t read; /* wait for read */
pthread_cond_t write; /* wait for write */ pthread_cond_t write; /* wait for write */
int valid; /* set when valid */ int valid; /* set when valid */
int r_active; /* readers active */ int r_active; /* readers active */
int w_active; /* writer active */ int w_active; /* writer active */
int r_wait; /* readers waiting */ int r_wait; /* readers waiting */
int w_wait; /* writers waiting */ int w_wait; /* writers waiting */
} rwlock_t; } rwlock_t;
#define RWLOCK_VALID 0xfacade #define RWLOCK_VALID 0xfacade
/* /*
* Support static initialization of barriers * Support static initialization of barriers
*/ */
#define RWL_INITIALIZER \ #define RWL_INITIALIZER \
{PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, \ { \
PTHREAD_COND_INITIALIZER, RWLOCK_VALID, 0, 0, 0, 0} PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, \
PTHREAD_COND_INITIALIZER, RWLOCK_VALID, 0, 0, 0, 0 \
}
/* /*
* Define read-write lock functions * Define read-write lock functions
*/ */
extern int rwl_init (rwlock_t *rwlock); extern int rwl_init(rwlock_t* rwlock);
extern int rwl_destroy (rwlock_t *rwlock); extern int rwl_destroy(rwlock_t* rwlock);
extern int rwl_readlock (rwlock_t *rwlock); extern int rwl_readlock(rwlock_t* rwlock);
extern int rwl_readtrylock (rwlock_t *rwlock); extern int rwl_readtrylock(rwlock_t* rwlock);
extern int rwl_readunlock (rwlock_t *rwlock); extern int rwl_readunlock(rwlock_t* rwlock);
extern int rwl_writelock (rwlock_t *rwlock); extern int rwl_writelock(rwlock_t* rwlock);
extern int rwl_writetrylock (rwlock_t *rwlock); extern int rwl_writetrylock(rwlock_t* rwlock);
extern int rwl_writeunlock (rwlock_t *rwlock); extern int rwl_writeunlock(rwlock_t* rwlock);

View File

@@ -8,31 +8,31 @@
* to allow interleaved thread execution, since threads are not * to allow interleaved thread execution, since threads are not
* timesliced. * timesliced.
*/ */
#include "rwlock.h"
#include "errors.h" #include "errors.h"
#include "rwlock.h"
#define THREADS 5 #define THREADS 5
#define DATASIZE 15 #define DATASIZE 15
#define ITERATIONS 10000 #define ITERATIONS 10000
/* /*
* Keep statistics for each thread. * Keep statistics for each thread.
*/ */
typedef struct thread_tag { typedef struct thread_tag {
int thread_num; int thread_num;
pthread_t thread_id; pthread_t thread_id;
int updates; int updates;
int reads; int reads;
int interval; int interval;
} thread_t; } thread_t;
/* /*
* Read-write lock and shared data * Read-write lock and shared data
*/ */
typedef struct data_tag { typedef struct data_tag {
rwlock_t lock; rwlock_t lock;
int data; int data;
int updates; int updates;
} data_t; } data_t;
thread_t threads[THREADS]; thread_t threads[THREADS];
@@ -41,127 +41,135 @@ data_t data[DATASIZE];
/* /*
* Thread start routine that uses read-write locks * Thread start routine that uses read-write locks
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
thread_t *self = (thread_t*)arg; thread_t* self = (thread_t*) arg;
int repeats = 0; int repeats = 0;
int iteration; int iteration;
int element = 0; int element = 0;
int status; int status;
for (iteration = 0; iteration < ITERATIONS; iteration++) { for (iteration = 0; iteration < ITERATIONS; iteration++) {
/* /*
* Each "self->interval" iterations, perform an * Each "self->interval" iterations, perform an
* update operation (write lock instead of read * update operation (write lock instead of read
* lock). * lock).
*/ */
if ((iteration % self->interval) == 0) { if ((iteration % self->interval) == 0) {
status = rwl_writelock (&data[element].lock); status = rwl_writelock(&data[element].lock);
if (status != 0) if (status != 0)
err_abort (status, "Write lock"); err_abort(status, "Write lock");
data[element].data = self->thread_num; data[element].data = self->thread_num;
data[element].updates++; data[element].updates++;
self->updates++; self->updates++;
status = rwl_writeunlock (&data[element].lock); status = rwl_writeunlock(&data[element].lock);
if (status != 0) if (status != 0)
err_abort (status, "Write unlock"); err_abort(status, "Write unlock");
} else {
/*
* Look at the current data element to see whether
* the current thread last updated it. Count the
* times, to report later.
*/
status = rwl_readlock (&data[element].lock);
if (status != 0)
err_abort (status, "Read lock");
self->reads++;
if (data[element].data == self->thread_num)
repeats++;
status = rwl_readunlock (&data[element].lock);
if (status != 0)
err_abort (status, "Read unlock");
}
element++;
if (element >= DATASIZE)
element = 0;
} }
else {
/*
* Look at the current data element to see whether
* the current thread last updated it. Count the
* times, to report later.
*/
status = rwl_readlock(&data[element].lock);
if (status != 0)
err_abort(status, "Read lock");
self->reads++;
if (data[element].data == self->thread_num)
repeats++;
status = rwl_readunlock(&data[element].lock);
if (status != 0)
err_abort(status, "Read unlock");
}
element++;
if (element >= DATASIZE)
element = 0;
}
if (repeats > 0) if (repeats > 0)
printf ( printf("Thread %d found unchanged elements %d times\n",
"Thread %d found unchanged elements %d times\n", self->thread_num,
self->thread_num, repeats); repeats);
return NULL; return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
int count; int count;
int data_count; int data_count;
int status; int status;
unsigned int seed = 1; unsigned int seed = 1;
int thread_updates = 0; int thread_updates = 0;
int data_updates = 0; int data_updates = 0;
#ifdef sun #ifdef sun
/* /*
* On Solaris 2.5, threads are not timesliced. To ensure * On Solaris 2.5, threads are not timesliced. To ensure
* that our threads can run concurrently, we need to * that our threads can run concurrently, we need to
* increase the concurrency level to THREADS. * increase the concurrency level to THREADS.
*/ */
DPRINTF (("Setting concurrency level to %d\n", THREADS)); DPRINTF(("Setting concurrency level to %d\n", THREADS));
thr_setconcurrency (THREADS); thr_setconcurrency(THREADS);
#endif #endif
/* /*
* Initialize the shared data. * Initialize the shared data.
*/ */
for (data_count = 0; data_count < DATASIZE; data_count++) { for (data_count = 0; data_count < DATASIZE; data_count++) {
data[data_count].data = 0; data[data_count].data = 0;
data[data_count].updates = 0; data[data_count].updates = 0;
status = rwl_init (&data[data_count].lock); status = rwl_init(&data[data_count].lock);
if (status != 0) if (status != 0)
err_abort (status, "Init rw lock"); err_abort(status, "Init rw lock");
} }
/* /*
* Create THREADS threads to access shared data. * Create THREADS threads to access shared data.
*/ */
for (count = 0; count < THREADS; count++) { for (count = 0; count < THREADS; count++) {
threads[count].thread_num = count; threads[count].thread_num = count;
threads[count].updates = 0; threads[count].updates = 0;
threads[count].reads = 0; threads[count].reads = 0;
threads[count].interval = rand_r (&seed) % 71; threads[count].interval = rand_r(&seed) % 71;
status = pthread_create (&threads[count].thread_id, status = pthread_create(&threads[count].thread_id,
NULL, thread_routine, (void*)&threads[count]); NULL,
if (status != 0) thread_routine,
err_abort (status, "Create thread"); (void*) &threads[count]);
} if (status != 0)
err_abort(status, "Create thread");
}
/* /*
* Wait for all threads to complete, and collect * Wait for all threads to complete, and collect
* statistics. * statistics.
*/ */
for (count = 0; count < THREADS; count++) { for (count = 0; count < THREADS; count++) {
status = pthread_join (threads[count].thread_id, NULL); status = pthread_join(threads[count].thread_id, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Join thread"); err_abort(status, "Join thread");
thread_updates += threads[count].updates; thread_updates += threads[count].updates;
printf ("%02d: interval %d, updates %d, reads %d\n", printf("%02d: interval %d, updates %d, reads %d\n",
count, threads[count].interval, count,
threads[count].updates, threads[count].reads); threads[count].interval,
} threads[count].updates,
threads[count].reads);
}
/* /*
* Collect statistics for the data. * Collect statistics for the data.
*/ */
for (data_count = 0; data_count < DATASIZE; data_count++) { for (data_count = 0; data_count < DATASIZE; data_count++) {
data_updates += data[data_count].updates; data_updates += data[data_count].updates;
printf ("data %02d: value %d, %d updates\n", printf("data %02d: value %d, %d updates\n",
data_count, data[data_count].data, data[data_count].updates); data_count,
rwl_destroy (&data[data_count].lock); data[data_count].data,
} data[data_count].updates);
rwl_destroy(&data[data_count].lock);
}
printf ("%d thread updates, %d data updates\n", printf("%d thread updates, %d data updates\n", thread_updates, data_updates);
thread_updates, data_updates); return 0;
return 0;
} }

View File

@@ -8,32 +8,32 @@
* timesliced. * timesliced.
*/ */
#include <pthread.h> #include <pthread.h>
#include "rwlock.h"
#include "errors.h" #include "errors.h"
#include "rwlock.h"
#define THREADS 5 #define THREADS 5
#define ITERATIONS 1000 #define ITERATIONS 1000
#define DATASIZE 15 #define DATASIZE 15
/* /*
* Keep statistics for each thread. * Keep statistics for each thread.
*/ */
typedef struct thread_tag { typedef struct thread_tag {
int thread_num; int thread_num;
pthread_t thread_id; pthread_t thread_id;
int r_collisions; int r_collisions;
int w_collisions; int w_collisions;
int updates; int updates;
int interval; int interval;
} thread_t; } thread_t;
/* /*
* Read-write lock and shared data * Read-write lock and shared data
*/ */
typedef struct data_tag { typedef struct data_tag {
rwlock_t lock; rwlock_t lock;
int data; int data;
int updates; int updates;
} data_t; } data_t;
thread_t threads[THREADS]; thread_t threads[THREADS];
@@ -42,115 +42,129 @@ data_t data[DATASIZE];
/* /*
* Thread start routine that uses read-write locks * Thread start routine that uses read-write locks
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
thread_t *self = (thread_t*)arg; thread_t* self = (thread_t*) arg;
int iteration; int iteration;
int element; int element;
int status; int status;
element = 0; /* Current data element */ element = 0; /* Current data element */
for (iteration = 0; iteration < ITERATIONS; iteration++) { for (iteration = 0; iteration < ITERATIONS; iteration++) {
if ((iteration % self->interval) == 0) { if ((iteration % self->interval) == 0) {
status = rwl_writetrylock (&data[element].lock); status = rwl_writetrylock(&data[element].lock);
if (status == EBUSY) if (status == EBUSY)
self->w_collisions++; self->w_collisions++;
else if (status == 0) { else if (status == 0) {
data[element].data++; data[element].data++;
data[element].updates++; data[element].updates++;
self->updates++; self->updates++;
rwl_writeunlock (&data[element].lock); rwl_writeunlock(&data[element].lock);
} else }
err_abort (status, "Try write lock"); else
} else { err_abort(status, "Try write lock");
status = rwl_readtrylock (&data[element].lock);
if (status == EBUSY)
self->r_collisions++;
else if (status != 0) {
err_abort (status, "Try read lock");
} else {
if (data[element].data != data[element].updates)
printf ("%d: data[%d] %d != %d\n",
self->thread_num, element,
data[element].data, data[element].updates);
rwl_readunlock (&data[element].lock);
}
}
element++;
if (element >= DATASIZE)
element = 0;
} }
return NULL; else {
status = rwl_readtrylock(&data[element].lock);
if (status == EBUSY)
self->r_collisions++;
else if (status != 0) {
err_abort(status, "Try read lock");
}
else {
if (data[element].data != data[element].updates)
printf("%d: data[%d] %d != %d\n",
self->thread_num,
element,
data[element].data,
data[element].updates);
rwl_readunlock(&data[element].lock);
}
}
element++;
if (element >= DATASIZE)
element = 0;
}
return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
int count, data_count; int count, data_count;
unsigned int seed = 1; unsigned int seed = 1;
int thread_updates = 0, data_updates = 0; int thread_updates = 0, data_updates = 0;
int status; int status;
#ifdef sun #ifdef sun
/* /*
* On Solaris 2.5, threads are not timesliced. To ensure * On Solaris 2.5, threads are not timesliced. To ensure
* that our threads can run concurrently, we need to * that our threads can run concurrently, we need to
* increase the concurrency level to THREADS. * increase the concurrency level to THREADS.
*/ */
DPRINTF (("Setting concurrency level to %d\n", THREADS)); DPRINTF(("Setting concurrency level to %d\n", THREADS));
thr_setconcurrency (THREADS); thr_setconcurrency(THREADS);
#endif #endif
/* /*
* Initialize the shared data. * Initialize the shared data.
*/ */
for (data_count = 0; data_count < DATASIZE; data_count++) { for (data_count = 0; data_count < DATASIZE; data_count++) {
data[data_count].data = 0; data[data_count].data = 0;
data[data_count].updates = 0; data[data_count].updates = 0;
rwl_init (&data[data_count].lock); rwl_init(&data[data_count].lock);
} }
/* /*
* Create THREADS threads to access shared data. * Create THREADS threads to access shared data.
*/ */
for (count = 0; count < THREADS; count++) { for (count = 0; count < THREADS; count++) {
threads[count].thread_num = count; threads[count].thread_num = count;
threads[count].r_collisions = 0; threads[count].r_collisions = 0;
threads[count].w_collisions = 0; threads[count].w_collisions = 0;
threads[count].updates = 0; threads[count].updates = 0;
threads[count].interval = rand_r (&seed) % ITERATIONS; threads[count].interval = rand_r(&seed) % ITERATIONS;
status = pthread_create (&threads[count].thread_id, status = pthread_create(&threads[count].thread_id,
NULL, thread_routine, (void*)&threads[count]); NULL,
if (status != 0) thread_routine,
err_abort (status, "Create thread"); (void*) &threads[count]);
} if (status != 0)
err_abort(status, "Create thread");
}
/* /*
* Wait for all threads to complete, and collect * Wait for all threads to complete, and collect
* statistics. * statistics.
*/ */
for (count = 0; count < THREADS; count++) { for (count = 0; count < THREADS; count++) {
status = pthread_join (threads[count].thread_id, NULL); status = pthread_join(threads[count].thread_id, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Join thread"); err_abort(status, "Join thread");
thread_updates += threads[count].updates; thread_updates += threads[count].updates;
printf ("%02d: interval %d, updates %d, " printf(
"r_collisions %d, w_collisions %d\n", "%02d: interval %d, updates %d, "
count, threads[count].interval, "r_collisions %d, w_collisions %d\n",
threads[count].updates, count,
threads[count].r_collisions, threads[count].w_collisions); threads[count].interval,
} threads[count].updates,
threads[count].r_collisions,
threads[count].w_collisions);
}
/* /*
* Collect statistics for the data. * Collect statistics for the data.
*/ */
for (data_count = 0; data_count < DATASIZE; data_count++) { for (data_count = 0; data_count < DATASIZE; data_count++) {
data_updates += data[data_count].updates; data_updates += data[data_count].updates;
printf ("data %02d: value %d, %d updates\n", printf("data %02d: value %d, %d updates\n",
data_count, data[data_count].data, data[data_count].updates); data_count,
rwl_destroy (&data[data_count].lock); data[data_count].data,
} data[data_count].updates);
rwl_destroy(&data[data_count].lock);
}
return 0; return 0;
} }

View File

@@ -20,288 +20,289 @@
* processing threads will begin to shut down. (They will be * processing threads will begin to shut down. (They will be
* restarted when work appears.) * restarted when work appears.)
*/ */
#include "workq.h"
#include <pthread.h> #include <pthread.h>
#include <stdlib.h> #include <stdlib.h>
#include <time.h> #include <time.h>
#include "errors.h" #include "errors.h"
#include "workq.h"
/* /*
* Thread start routine to serve the work queue. * Thread start routine to serve the work queue.
*/ */
static void *workq_server (void *arg) static void*
workq_server(void* arg)
{ {
struct timespec timeout; struct timespec timeout;
workq_t *wq = (workq_t *)arg; workq_t* wq = (workq_t*) arg;
workq_ele_t *we; workq_ele_t* we;
int status, timedout; int status, timedout;
/* /*
* We don't need to validate the workq_t here... we don't * We don't need to validate the workq_t here... we don't
* create server threads until requests are queued (the * create server threads until requests are queued (the
* queue has been initialized by then!) and we wait for all * queue has been initialized by then!) and we wait for all
* server threads to terminate before destroying a work * server threads to terminate before destroying a work
* queue. * queue.
*/ */
DPRINTF (("A worker is starting\n")); DPRINTF(("A worker is starting\n"));
status = pthread_mutex_lock (&wq->mutex); status = pthread_mutex_lock(&wq->mutex);
if (status != 0) if (status != 0)
return NULL;
while (1) {
timedout = 0;
DPRINTF(("Worker waiting for work\n"));
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += 2;
while (wq->first == NULL && !wq->quit) {
/*
* Server threads time out after spending 2 seconds
* waiting for new work, and exit.
*/
status = pthread_cond_timedwait(&wq->cv, &wq->mutex, &timeout);
if (status == ETIMEDOUT) {
DPRINTF(("Worker wait timed out\n"));
timedout = 1;
break;
}
else if (status != 0) {
/*
* This shouldn't happen, so the work queue
* package should fail. Because the work queue
* API is asynchronous, that would add
* complication. Because the chances of failure
* are slim, I choose to avoid that
* complication. The server thread will return,
* and allow another server thread to pick up
* the work later. Note that, if this was the
* only server thread, the queue won't be
* serviced until a new work item is
* queued. That could be fixed by creating a new
* server here.
*/
DPRINTF(("Worker wait failed, %d (%s)\n", status, strerror(status)));
wq->counter--;
pthread_mutex_unlock(&wq->mutex);
return NULL; return NULL;
}
}
DPRINTF(("Work queue: %#lx, quit: %d\n", wq->first, wq->quit));
we = wq->first;
while (1) { if (we != NULL) {
timedout = 0; wq->first = we->next;
DPRINTF (("Worker waiting for work\n")); if (wq->last == we)
clock_gettime (CLOCK_REALTIME, &timeout); wq->last = NULL;
timeout.tv_sec += 2; status = pthread_mutex_unlock(&wq->mutex);
if (status != 0)
while (wq->first == NULL && !wq->quit) { return NULL;
/* DPRINTF(("Worker calling engine\n"));
* Server threads time out after spending 2 seconds wq->engine(we->data);
* waiting for new work, and exit. free(we);
*/ status = pthread_mutex_lock(&wq->mutex);
status = pthread_cond_timedwait ( if (status != 0)
&wq->cv, &wq->mutex, &timeout); return NULL;
if (status == ETIMEDOUT) {
DPRINTF (("Worker wait timed out\n"));
timedout = 1;
break;
} else if (status != 0) {
/*
* This shouldn't happen, so the work queue
* package should fail. Because the work queue
* API is asynchronous, that would add
* complication. Because the chances of failure
* are slim, I choose to avoid that
* complication. The server thread will return,
* and allow another server thread to pick up
* the work later. Note that, if this was the
* only server thread, the queue won't be
* serviced until a new work item is
* queued. That could be fixed by creating a new
* server here.
*/
DPRINTF ((
"Worker wait failed, %d (%s)\n",
status, strerror (status)));
wq->counter--;
pthread_mutex_unlock (&wq->mutex);
return NULL;
}
}
DPRINTF (("Work queue: %#lx, quit: %d\n", wq->first, wq->quit));
we = wq->first;
if (we != NULL) {
wq->first = we->next;
if (wq->last == we)
wq->last = NULL;
status = pthread_mutex_unlock (&wq->mutex);
if (status != 0)
return NULL;
DPRINTF (("Worker calling engine\n"));
wq->engine (we->data);
free (we);
status = pthread_mutex_lock (&wq->mutex);
if (status != 0)
return NULL;
}
/*
* If there are no more work requests, and the servers
* have been asked to quit, then shut down.
*/
if (wq->first == NULL && wq->quit) {
DPRINTF (("Worker shutting down\n"));
wq->counter--;
/*
* NOTE: Just to prove that every rule has an
* exception, I'm using the "cv" condition for two
* separate predicates here. That's OK, since the
* case used here applies only once during the life
* of a work queue -- during rundown. The overhead
* is minimal and it's not worth creating a separate
* condition variable that would be waited and
* signaled exactly once!
*/
if (wq->counter == 0)
pthread_cond_broadcast (&wq->cv);
pthread_mutex_unlock (&wq->mutex);
return NULL;
}
/*
* If there's no more work, and we wait for as long as
* we're allowed, then terminate this server thread.
*/
if (wq->first == NULL && timedout) {
DPRINTF (("engine terminating due to timeout.\n"));
wq->counter--;
break;
}
} }
pthread_mutex_unlock (&wq->mutex); /*
DPRINTF (("Worker exiting\n")); * If there are no more work requests, and the servers
return NULL; * have been asked to quit, then shut down.
*/
if (wq->first == NULL && wq->quit) {
DPRINTF(("Worker shutting down\n"));
wq->counter--;
/*
* NOTE: Just to prove that every rule has an
* exception, I'm using the "cv" condition for two
* separate predicates here. That's OK, since the
* case used here applies only once during the life
* of a work queue -- during rundown. The overhead
* is minimal and it's not worth creating a separate
* condition variable that would be waited and
* signaled exactly once!
*/
if (wq->counter == 0)
pthread_cond_broadcast(&wq->cv);
pthread_mutex_unlock(&wq->mutex);
return NULL;
}
/*
* If there's no more work, and we wait for as long as
* we're allowed, then terminate this server thread.
*/
if (wq->first == NULL && timedout) {
DPRINTF(("engine terminating due to timeout.\n"));
wq->counter--;
break;
}
}
pthread_mutex_unlock(&wq->mutex);
DPRINTF(("Worker exiting\n"));
return NULL;
} }
/* /*
* Initialize a work queue. * Initialize a work queue.
*/ */
int workq_init (workq_t *wq, int threads, void (*engine)(void *arg)) int
workq_init(workq_t* wq, int threads, void (*engine)(void* arg))
{ {
int status; int status;
status = pthread_attr_init (&wq->attr); status = pthread_attr_init(&wq->attr);
if (status != 0) if (status != 0)
return status; return status;
status = pthread_attr_setdetachstate ( status = pthread_attr_setdetachstate(&wq->attr, PTHREAD_CREATE_DETACHED);
&wq->attr, PTHREAD_CREATE_DETACHED); if (status != 0) {
if (status != 0) { pthread_attr_destroy(&wq->attr);
pthread_attr_destroy (&wq->attr); return status;
return status; }
} status = pthread_mutex_init(&wq->mutex, NULL);
status = pthread_mutex_init (&wq->mutex, NULL); if (status != 0) {
if (status != 0) { pthread_attr_destroy(&wq->attr);
pthread_attr_destroy (&wq->attr); return status;
return status; }
} status = pthread_cond_init(&wq->cv, NULL);
status = pthread_cond_init (&wq->cv, NULL); if (status != 0) {
if (status != 0) { pthread_mutex_destroy(&wq->mutex);
pthread_mutex_destroy (&wq->mutex); pthread_attr_destroy(&wq->attr);
pthread_attr_destroy (&wq->attr); return status;
return status; }
} wq->quit = 0; /* not time to quit */
wq->quit = 0; /* not time to quit */ wq->first = wq->last = NULL; /* no queue entries */
wq->first = wq->last = NULL; /* no queue entries */ wq->parallelism = threads; /* max servers */
wq->parallelism = threads; /* max servers */ wq->counter = 0; /* no server threads yet */
wq->counter = 0; /* no server threads yet */ wq->idle = 0; /* no idle servers */
wq->idle = 0; /* no idle servers */ wq->engine = engine;
wq->engine = engine; wq->valid = WORKQ_VALID;
wq->valid = WORKQ_VALID; return 0;
return 0;
} }
/* /*
* Destroy a work queue. * Destroy a work queue.
*/ */
int workq_destroy (workq_t *wq) int
workq_destroy(workq_t* wq)
{ {
int status, status1, status2; int status, status1, status2;
if (wq->valid != WORKQ_VALID) if (wq->valid != WORKQ_VALID)
return EINVAL; return EINVAL;
status = pthread_mutex_lock (&wq->mutex); status = pthread_mutex_lock(&wq->mutex);
if (status != 0) if (status != 0)
return status;
wq->valid = 0; /* prevent any other operations */
/*
* Check whether any threads are active, and run them down:
*
* 1. set the quit flag
* 2. broadcast to wake any servers that may be asleep
* 4. wait for all threads to quit (counter goes to 0)
* Because we don't use join, we don't need to worry
* about tracking thread IDs.
*/
if (wq->counter > 0) {
wq->quit = 1;
/* if any threads are idling, wake them. */
if (wq->idle > 0) {
status = pthread_cond_broadcast(&wq->cv);
if (status != 0) {
pthread_mutex_unlock(&wq->mutex);
return status; return status;
wq->valid = 0; /* prevent any other operations */ }
}
/* /*
* Check whether any threads are active, and run them down: * Just to prove that every rule has an exception, I'm
* * using the "cv" condition for two separate predicates
* 1. set the quit flag * here. That's OK, since the case used here applies
* 2. broadcast to wake any servers that may be asleep * only once during the life of a work queue -- during
* 4. wait for all threads to quit (counter goes to 0) * rundown. The overhead is minimal and it's not worth
* Because we don't use join, we don't need to worry * creating a separate condition variable that would be
* about tracking thread IDs. * waited and signalled exactly once!
*/ */
if (wq->counter > 0) { while (wq->counter > 0) {
wq->quit = 1; status = pthread_cond_wait(&wq->cv, &wq->mutex);
/* if any threads are idling, wake them. */ if (status != 0) {
if (wq->idle > 0) { pthread_mutex_unlock(&wq->mutex);
status = pthread_cond_broadcast (&wq->cv);
if (status != 0) {
pthread_mutex_unlock (&wq->mutex);
return status;
}
}
/*
* Just to prove that every rule has an exception, I'm
* using the "cv" condition for two separate predicates
* here. That's OK, since the case used here applies
* only once during the life of a work queue -- during
* rundown. The overhead is minimal and it's not worth
* creating a separate condition variable that would be
* waited and signalled exactly once!
*/
while (wq->counter > 0) {
status = pthread_cond_wait (&wq->cv, &wq->mutex);
if (status != 0) {
pthread_mutex_unlock (&wq->mutex);
return status;
}
}
}
status = pthread_mutex_unlock (&wq->mutex);
if (status != 0)
return status; return status;
status = pthread_mutex_destroy (&wq->mutex); }
status1 = pthread_cond_destroy (&wq->cv); }
status2 = pthread_attr_destroy (&wq->attr); }
return (status ? status : (status1 ? status1 : status2)); status = pthread_mutex_unlock(&wq->mutex);
if (status != 0)
return status;
status = pthread_mutex_destroy(&wq->mutex);
status1 = pthread_cond_destroy(&wq->cv);
status2 = pthread_attr_destroy(&wq->attr);
return (status ? status : (status1 ? status1 : status2));
} }
/* /*
* Add an item to a work queue. * Add an item to a work queue.
*/ */
int workq_add (workq_t *wq, void *element) int
workq_add(workq_t* wq, void* element)
{ {
workq_ele_t *item; workq_ele_t* item;
pthread_t id; pthread_t id;
int status; int status;
if (wq->valid != WORKQ_VALID) if (wq->valid != WORKQ_VALID)
return EINVAL; return EINVAL;
/* /*
* Create and initialize a request structure. * Create and initialize a request structure.
*/ */
item = (workq_ele_t *)malloc (sizeof (workq_ele_t)); item = (workq_ele_t*) malloc(sizeof(workq_ele_t));
if (item == NULL) if (item == NULL)
return ENOMEM; return ENOMEM;
item->data = element; item->data = element;
item->next = NULL; item->next = NULL;
status = pthread_mutex_lock (&wq->mutex); status = pthread_mutex_lock(&wq->mutex);
if (status != 0) {
free(item);
return status;
}
/*
* Add the request to the end of the queue, updating the
* first and last pointers.
*/
if (wq->first == NULL)
wq->first = item;
else
wq->last->next = item;
wq->last = item;
/*
* if any threads are idling, wake one.
*/
if (wq->idle > 0) {
status = pthread_cond_signal(&wq->cv);
if (status != 0) { if (status != 0) {
free (item); pthread_mutex_unlock(&wq->mutex);
return status; return status;
} }
}
else if (wq->counter < wq->parallelism) {
/* /*
* Add the request to the end of the queue, updating the * If there were no idling threads, and we're allowed to
* first and last pointers. * create a new thread, do so.
*/ */
if (wq->first == NULL) DPRINTF(("Creating new worker\n"));
wq->first = item; status = pthread_create(&id, &wq->attr, workq_server, (void*) wq);
else if (status != 0) {
wq->last->next = item; pthread_mutex_unlock(&wq->mutex);
wq->last = item; return status;
/*
* if any threads are idling, wake one.
*/
if (wq->idle > 0) {
status = pthread_cond_signal (&wq->cv);
if (status != 0) {
pthread_mutex_unlock (&wq->mutex);
return status;
}
} else if (wq->counter < wq->parallelism) {
/*
* If there were no idling threads, and we're allowed to
* create a new thread, do so.
*/
DPRINTF (("Creating new worker\n"));
status = pthread_create (
&id, &wq->attr, workq_server, (void*)wq);
if (status != 0) {
pthread_mutex_unlock (&wq->mutex);
return status;
}
wq->counter++;
} }
pthread_mutex_unlock (&wq->mutex); wq->counter++;
return 0; }
pthread_mutex_unlock(&wq->mutex);
return 0;
} }

View File

@@ -26,34 +26,33 @@
* Structure to keep track of work queue requests. * Structure to keep track of work queue requests.
*/ */
typedef struct workq_ele_tag { typedef struct workq_ele_tag {
struct workq_ele_tag *next; struct workq_ele_tag* next;
void *data; void* data;
} workq_ele_t; } workq_ele_t;
/* /*
* Structure describing a work queue. * Structure describing a work queue.
*/ */
typedef struct workq_tag { typedef struct workq_tag {
pthread_mutex_t mutex; pthread_mutex_t mutex;
pthread_cond_t cv; /* wait for work */ pthread_cond_t cv; /* wait for work */
pthread_attr_t attr; /* create detached threads */ pthread_attr_t attr; /* create detached threads */
workq_ele_t *first, *last; /* work queue */ workq_ele_t *first, *last; /* work queue */
int valid; /* set when valid */ int valid; /* set when valid */
int quit; /* set when workq should quit */ int quit; /* set when workq should quit */
int parallelism; /* number of threads required */ int parallelism; /* number of threads required */
int counter; /* current number of threads */ int counter; /* current number of threads */
int idle; /* number of idle threads */ int idle; /* number of idle threads */
void (*engine)(void *arg); /* user engine */ void (*engine)(void* arg); /* user engine */
} workq_t; } workq_t;
#define WORKQ_VALID 0xdec1992 #define WORKQ_VALID 0xdec1992
/* /*
* Define work queue functions * Define work queue functions
*/ */
extern int workq_init ( extern int workq_init(workq_t* wq,
workq_t *wq, int threads, /* maximum threads */
int threads, /* maximum threads */ void (*engine)(void*)); /* engine routine */
void (*engine)(void *)); /* engine routine */ extern int workq_destroy(workq_t* wq);
extern int workq_destroy (workq_t *wq); extern int workq_add(workq_t* wq, void* data);
extern int workq_add (workq_t *wq, void *data);

View File

@@ -4,141 +4,139 @@
* Demonstrate a use of work queues. * Demonstrate a use of work queues.
*/ */
#include <pthread.h> #include <pthread.h>
#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <time.h> #include <time.h>
#include "workq.h"
#include "errors.h" #include "errors.h"
#include "workq.h"
#define ITERATIONS 25 #define ITERATIONS 25
typedef struct power_tag { typedef struct power_tag {
int value; int value;
int power; int power;
} power_t; } power_t;
typedef struct engine_tag { typedef struct engine_tag {
struct engine_tag *link; struct engine_tag* link;
pthread_t thread_id; pthread_t thread_id;
int calls; int calls;
} engine_t; } engine_t;
pthread_key_t engine_key; /* Keep track of active engines */ pthread_key_t engine_key; /* Keep track of active engines */
pthread_mutex_t engine_list_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t engine_list_mutex = PTHREAD_MUTEX_INITIALIZER;
engine_t *engine_list_head = NULL; engine_t* engine_list_head = NULL;
workq_t workq; workq_t workq;
/* /*
* Thread-specific data destructor routine for engine_key * Thread-specific data destructor routine for engine_key
*/ */
void destructor (void *value_ptr) void
destructor(void* value_ptr)
{ {
engine_t *engine = (engine_t*)value_ptr; engine_t* engine = (engine_t*) value_ptr;
pthread_mutex_lock (&engine_list_mutex); pthread_mutex_lock(&engine_list_mutex);
engine->link = engine_list_head; engine->link = engine_list_head;
engine_list_head = engine; engine_list_head = engine;
pthread_mutex_unlock (&engine_list_mutex); pthread_mutex_unlock(&engine_list_mutex);
} }
/* /*
* This is the routine called by the work queue servers to * This is the routine called by the work queue servers to
* perform operations in parallel. * perform operations in parallel.
*/ */
void engine_routine (void *arg) void
engine_routine(void* arg)
{ {
engine_t *engine; engine_t* engine;
power_t *power = (power_t*)arg; power_t* power = (power_t*) arg;
int result, count; int result, count;
int status; int status;
engine = pthread_getspecific (engine_key); engine = pthread_getspecific(engine_key);
if (engine == NULL) { if (engine == NULL) {
engine = (engine_t*)malloc (sizeof (engine_t)); engine = (engine_t*) malloc(sizeof(engine_t));
status = pthread_setspecific ( status = pthread_setspecific(engine_key, (void*) engine);
engine_key, (void*)engine); if (status != 0)
if (status != 0) err_abort(status, "Set tsd");
err_abort (status, "Set tsd"); engine->thread_id = pthread_self();
engine->thread_id = pthread_self (); engine->calls = 1;
engine->calls = 1; }
} else else
engine->calls++; engine->calls++;
result = 1; result = 1;
printf ( printf("Engine: computing %d^%d\n", power->value, power->power);
"Engine: computing %d^%d\n", for (count = 1; count <= power->power; count++) result *= power->value;
power->value, power->power); free(arg);
for (count = 1; count <= power->power; count++)
result *= power->value;
free (arg);
} }
/* /*
* Thread start routine that issues work queue requests. * Thread start routine that issues work queue requests.
*/ */
void *thread_routine (void *arg) void*
thread_routine(void* arg)
{ {
power_t *element; power_t* element;
int count; int count;
unsigned int seed = (unsigned int)time (NULL); unsigned int seed = (unsigned int) time(NULL);
int status; int status;
/* /*
* Loop, making requests. * Loop, making requests.
*/ */
for (count = 0; count < ITERATIONS; count++) { for (count = 0; count < ITERATIONS; count++) {
element = (power_t*)malloc (sizeof (power_t)); element = (power_t*) malloc(sizeof(power_t));
if (element == NULL) if (element == NULL)
errno_abort ("Allocate element"); errno_abort("Allocate element");
element->value = rand_r (&seed) % 20; element->value = rand_r(&seed) % 20;
element->power = rand_r (&seed) % 7; element->power = rand_r(&seed) % 7;
DPRINTF (( DPRINTF(("Request: %d^%d\n", element->value, element->power));
"Request: %d^%d\n", status = workq_add(&workq, (void*) element);
element->value, element->power)); if (status != 0)
status = workq_add (&workq, (void*)element); err_abort(status, "Add to work queue");
if (status != 0) sleep(rand_r(&seed) % 5);
err_abort (status, "Add to work queue"); }
sleep (rand_r (&seed) % 5); return NULL;
}
return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t thread_id; pthread_t thread_id;
engine_t *engine; engine_t* engine;
int count = 0, calls = 0; int count = 0, calls = 0;
int status; int status;
status = pthread_key_create (&engine_key, destructor); status = pthread_key_create(&engine_key, destructor);
if (status != 0) if (status != 0)
err_abort (status, "Create key"); err_abort(status, "Create key");
status = workq_init (&workq, 4, engine_routine); status = workq_init(&workq, 4, engine_routine);
if (status != 0) if (status != 0)
err_abort (status, "Init work queue"); err_abort(status, "Init work queue");
status = pthread_create (&thread_id, NULL, thread_routine, NULL); status = pthread_create(&thread_id, NULL, thread_routine, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Create thread"); err_abort(status, "Create thread");
(void)thread_routine (NULL); (void) thread_routine(NULL);
status = pthread_join (thread_id, NULL); status = pthread_join(thread_id, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Join thread"); err_abort(status, "Join thread");
status = workq_destroy (&workq); status = workq_destroy(&workq);
if (status != 0) if (status != 0)
err_abort (status, "Destroy work queue"); err_abort(status, "Destroy work queue");
/* /*
* By now, all of the engine_t structures have been placed * By now, all of the engine_t structures have been placed
* on the list (by the engine thread destructors), so we * on the list (by the engine thread destructors), so we
* can count and summarize them. * can count and summarize them.
*/ */
engine = engine_list_head; engine = engine_list_head;
while (engine != NULL) { while (engine != NULL) {
count++; count++;
calls += engine->calls; calls += engine->calls;
printf ("engine %d: %d calls\n", count, engine->calls); printf("engine %d: %d calls\n", count, engine->calls);
engine = engine->link; engine = engine->link;
} }
printf ("%d engine threads processed %d calls\n", printf("%d engine threads processed %d calls\n", count, calls);
count, calls); return 0;
return 0;
} }

View File

@@ -11,46 +11,49 @@
#include <pthread.h> #include <pthread.h>
#include "errors.h" #include "errors.h"
void *printer_thread (void *arg) void*
printer_thread(void* arg)
{ {
char *string = *(char**)arg; char* string = *(char**) arg;
printf ("%s\n", string); printf("%s\n", string);
return NULL; return NULL;
} }
int main (int argc, char *argv[]) int
main(int argc, char* argv[])
{ {
pthread_t printer_id; pthread_t printer_id;
char *string_ptr; char* string_ptr;
int i, status; int i, status;
#ifdef sun #ifdef sun
/* /*
* On Solaris 2.5, threads are not timesliced. To ensure * On Solaris 2.5, threads are not timesliced. To ensure
* that our two threads can run concurrently, we need to * that our two threads can run concurrently, we need to
* increase the concurrency level to 2. * increase the concurrency level to 2.
*/ */
DPRINTF (("Setting concurrency level to 2\n")); DPRINTF(("Setting concurrency level to 2\n"));
thr_setconcurrency (2); thr_setconcurrency(2);
#endif #endif
string_ptr = "Before value"; string_ptr = "Before value";
status = pthread_create ( status =
&printer_id, NULL, printer_thread, (void*)&string_ptr); pthread_create(&printer_id, NULL, printer_thread, (void*) &string_ptr);
if (status != 0) if (status != 0)
err_abort (status, "Create thread"); err_abort(status, "Create thread");
/* /*
* Give the thread a chance to get started if it's going to run * Give the thread a chance to get started if it's going to run
* in parallel, but not enough that the current thread is likely * in parallel, but not enough that the current thread is likely
* to be timesliced. (This is a tricky balance, and the loop may * to be timesliced. (This is a tricky balance, and the loop may
* need to be adjusted on your system before you can see the bug.) * need to be adjusted on your system before you can see the bug.)
*/ */
for (i = 0; i < 10000000; i++); for (i = 0; i < 10000000; i++)
;
string_ptr = "After value"; string_ptr = "After value";
status = pthread_join (printer_id, NULL); status = pthread_join(printer_id, NULL);
if (status != 0) if (status != 0)
err_abort (status, "Join thread"); err_abort(status, "Join thread");
return 0; return 0;
} }