run clang-format
This commit is contained in:
104
.clang-format
Normal file
104
.clang-format
Normal 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
|
||||||
8
Makefile
8
Makefile
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
129
src/ch03/cond.c
129
src/ch03/cond.c
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
773
src/ch04/crew.c
773
src/ch04/crew.c
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
351
src/ch04/pipe.c
351
src/ch04/pipe.c
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
117
src/ch06/flock.c
117
src/ch06/flock.c
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
464
src/ch06/susp.c
464
src/ch06/susp.c
@@ -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 */
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
477
src/ch07/workq.c
477
src/ch07/workq.c
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user