| 1 | /*
|
|---|
| 2 | Program that uses mutex locking and unlocking
|
|---|
| 3 |
|
|---|
| 4 | In this program, multiple threads attempt to lock and unlock a mutex using
|
|---|
| 5 | the pthread_mutex_lock() function multiple times. These attempts cause each
|
|---|
| 6 | thread to wait for the mutex to be unlocked. This example shows how a mutex
|
|---|
| 7 | can be used to allow the creation of a simple thread-safe resource that is
|
|---|
| 8 | scoped to a process. It also shows how too much serialization can decrease
|
|---|
| 9 | the throughput of a multithreaded process.
|
|---|
| 10 |
|
|---|
| 11 | Choose your browser's option to save to local disk to download this code example.
|
|---|
| 12 | Send the program to your AS/400 and compile it using the development facilities
|
|---|
| 13 | supplied there. This program was developed and tested on V4R4.
|
|---|
| 14 |
|
|---|
| 15 | This small program that is furnished by IBM is a simple example to provide an
|
|---|
| 16 | illustration. This example has not been thoroughly tested under all conditions.
|
|---|
| 17 | IBM, therefore, cannot guarantee or imply reliability, serviceability, or function
|
|---|
| 18 | of this program. All programs contained herein are provided to you "AS IS".
|
|---|
| 19 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|---|
| 20 | ARE EXPRESSLY DISCLAIMED.
|
|---|
| 21 |
|
|---|
| 22 | */
|
|---|
| 23 | /********************************************************************/
|
|---|
| 24 | /* */
|
|---|
| 25 | /* test case: mutex_lck.c */
|
|---|
| 26 | /* */
|
|---|
| 27 | /* objective: Show semantics of pthread_mutex_lock() and */
|
|---|
| 28 | /* pthread_mutex_unlock() */
|
|---|
| 29 | /* */
|
|---|
| 30 | /* scenario: Create pthread_mutex */
|
|---|
| 31 | /* Lock mutex */
|
|---|
| 32 | /* create a process scoped resource to serialize */
|
|---|
| 33 | /* access to with the mutex. */
|
|---|
| 34 | /* Create THREAD_COUNT lock threads */
|
|---|
| 35 | /* Wait for all lock threads to start. */
|
|---|
| 36 | /* Each lock thread trys to lock mutex (blocks)*/
|
|---|
| 37 | /* Unlock mutex - causes a thread to lock mutex */
|
|---|
| 38 | /* After aquireing mutex, lock thread uses */
|
|---|
| 39 | /* the process scoped resource. */
|
|---|
| 40 | /* Each lock thread delays and unlocks mutex */
|
|---|
| 41 | /* Each Lock thread repeats lock & unlock */
|
|---|
| 42 | /* for a DOIT_COUNT times & exits */
|
|---|
| 43 | /* Join to all threads */
|
|---|
| 44 | /* Show the non corrupted resource after multiple */
|
|---|
| 45 | /* thread access. */
|
|---|
| 46 | /* Destroy mutex */
|
|---|
| 47 | /* */
|
|---|
| 48 | /* description: This demo is a valid case to have multiple */
|
|---|
| 49 | /* threads attempt to lock and unlock a mutex via */
|
|---|
| 50 | /* pthread_mutex_lock() multiple times. */
|
|---|
| 51 | /* causes each to wait for the unlock of the */
|
|---|
| 52 | /* mutex. It shows how a mutex is used to allow */
|
|---|
| 53 | /* the creation of a simple thread safe process */
|
|---|
| 54 | /* scoped resource. */
|
|---|
| 55 | /* This demo also shows how too much serialization*/
|
|---|
| 56 | /* can decrease the throughput of a multithreaded */
|
|---|
| 57 | /* process */
|
|---|
| 58 | /* */
|
|---|
| 59 | /* internal routines: lock_thread() */
|
|---|
| 60 | /* */
|
|---|
| 61 | /* external routines: pthread_create() */
|
|---|
| 62 | /* pthread_join() */
|
|---|
| 63 | /* pthread_cancel() */
|
|---|
| 64 | /* pthread_detach() */
|
|---|
| 65 | /* pthread_mutex_init() */
|
|---|
| 66 | /* pthread_mutex_destroy() */
|
|---|
| 67 | /* pthread_mutex_lock() */
|
|---|
| 68 | /* pthread_mutex_unlock() */
|
|---|
| 69 | /* gettimeofday() */
|
|---|
| 70 | /* sleep() */
|
|---|
| 71 | /* */
|
|---|
| 72 | /* usage notes: Compile this program using */
|
|---|
| 73 | /* CRTCMOD DEFINE('_MULTI_THREADED') and CRTPGM */
|
|---|
| 74 | /* Call it with no parameters. */
|
|---|
| 75 | /* When using kernel threads on you must start a */
|
|---|
| 76 | /* threaded program by using spawn(). You must set a */
|
|---|
| 77 | /* special flag in the inheritance structure that */
|
|---|
| 78 | /* causes spawn() to make the child process enabled */
|
|---|
| 79 | /* for kernel threads. There is no support for */
|
|---|
| 80 | /* running a threaded application in an interactive */
|
|---|
| 81 | /* job. This means that a threaded application cannot */
|
|---|
| 82 | /* use the terminal for user interaction. */
|
|---|
| 83 | /* Call it with no parameters. Output goes only */
|
|---|
| 84 | /* to the screen. To have output go to the CPA trace */
|
|---|
| 85 | /* user space and allow viewing via */
|
|---|
| 86 | /* DSPCPATRC TYPE(*KERNEL) */
|
|---|
| 87 | /* uncomment the line '#define CPATRC_OUTPUT' below. */
|
|---|
| 88 | /* */
|
|---|
| 89 | /********************************************************************/
|
|---|
| 90 |
|
|---|
| 91 | #include <pthread.h>
|
|---|
| 92 | #include <errno.h>
|
|---|
| 93 | #include <qp0ztrc.h>
|
|---|
| 94 | #include <stdio.h>
|
|---|
| 95 | #include <string.h>
|
|---|
| 96 | #include <stdlib.h>
|
|---|
| 97 | #include <time.h>
|
|---|
| 98 | #include <math.h>
|
|---|
| 99 | #include <unistd.h> /* for usleep */
|
|---|
| 100 |
|
|---|
| 101 | /* Uncomment the following line to have all output go to the */
|
|---|
| 102 | /* CPA trace user space so it can be displayed with */
|
|---|
| 103 | /* DSPCPATRC TYPE(*KERNEL) */
|
|---|
| 104 | #define CPATRC_OUTPUT
|
|---|
| 105 | #ifdef CPATRC_OUPUT
|
|---|
| 106 | #undef printf
|
|---|
| 107 | #define printf Qp0zUprintf
|
|---|
| 108 | #endif
|
|---|
| 109 |
|
|---|
| 110 | /* Global constants */
|
|---|
| 111 | #define THREAD_COUNT 5
|
|---|
| 112 | #define DOIT_COUNT 5
|
|---|
| 113 |
|
|---|
| 114 | /* Prototype of child threads, using the prototype to match the */
|
|---|
| 115 | /* pthread_startroutine_t is easiest */
|
|---|
| 116 | static void *lock_thread(void *);
|
|---|
| 117 |
|
|---|
| 118 | /* Global variables for all threads to access */
|
|---|
| 119 | static pthread_mutex_t mymutex;
|
|---|
| 120 | /* some resource that all threads are using. Access is serialized */
|
|---|
| 121 | /* using the mutex */
|
|---|
| 122 | volatile static int resource;
|
|---|
| 123 |
|
|---|
| 124 | /* Any information can be passed to the thread start routine */
|
|---|
| 125 | typedef struct {
|
|---|
| 126 | int tnum; /* Thread number */
|
|---|
| 127 | char string[50]; /* Short description */
|
|---|
| 128 | } threadparmdata_t;
|
|---|
| 129 |
|
|---|
| 130 | /* The main routine creates a process scoped resource and mutex */
|
|---|
| 131 | /* protecting that resource from multiple threads accessing it. */
|
|---|
| 132 | /* it then creates THREAD_COUNT threads who will manipulate the */
|
|---|
| 133 | /* resource. */
|
|---|
| 134 | /* After all threads have completed, the resource will be displayed */
|
|---|
| 135 | /* no data corruption should result on the resource because of the */
|
|---|
| 136 | /* mutex to synchronize access to it */
|
|---|
| 137 | int main (int argc, char *argv[])
|
|---|
| 138 | {
|
|---|
| 139 | int status, dstatus;
|
|---|
| 140 | int i;
|
|---|
| 141 | int *join_status;
|
|---|
| 142 | pthread_t threads[THREAD_COUNT];
|
|---|
| 143 | threadparmdata_t threadparms[THREAD_COUNT];
|
|---|
| 144 | struct timeval current_time;
|
|---|
| 145 |
|
|---|
| 146 |
|
|---|
| 147 | printf("main: Entering %s\n", argv[0]);
|
|---|
| 148 |
|
|---|
| 149 | printf("main: Create a mutex\n");
|
|---|
| 150 | status = pthread_mutex_init(&mymutex, NULL);
|
|---|
| 151 | if (status == -1) {
|
|---|
| 152 | printf("main: Create mutex failed = %d\n", errno);
|
|---|
| 153 | }
|
|---|
| 154 |
|
|---|
| 155 | gettimeofday(¤t_time, NULL);
|
|---|
| 156 | printf("main: Time before attempting to lock = %d\n",
|
|---|
| 157 | current_time.tv_sec);
|
|---|
| 158 | status = pthread_mutex_lock(&mymutex);
|
|---|
| 159 | if ( status != 0)
|
|---|
| 160 | {
|
|---|
| 161 | printf("main: Lock mutex failed, errno = %d\n", errno);
|
|---|
| 162 | /* Try to destroy mutex. NOTE: it may fail if another thread */
|
|---|
| 163 | /* is holding the mutex. At this point, we don't care, _BUT_ */
|
|---|
| 164 | /* If this mutex destroy fails and the CPA process is exited with*/
|
|---|
| 165 | /* the mutex still existing, a Synchronization VLOG will most */
|
|---|
| 166 | /* likely be cut indicating this. mutexes should be destroyed by*/
|
|---|
| 167 | /* the application program and not allowed to simply go out of */
|
|---|
| 168 | /* scope and be desctroyed by the system. The system treats this */
|
|---|
| 169 | /* as an abnormal condition */
|
|---|
| 170 | ( void )pthread_mutex_destroy(&mymutex);
|
|---|
| 171 | return(1);
|
|---|
| 172 | } /* endif */
|
|---|
| 173 |
|
|---|
| 174 | printf("main: This thread is holding the mutex and can manipulate the\n"
|
|---|
| 175 | "main: resource at will without concern for other threads\n"
|
|---|
| 176 | "main: interacting in evil ways with the resource\n");
|
|---|
| 177 | resource = 0;
|
|---|
| 178 | printf("main: Create threads that will block on locking the mutex\n");
|
|---|
| 179 | /* Loop to create all secondary threads */
|
|---|
| 180 | for ( i = 0; i < THREAD_COUNT ; i++ )
|
|---|
| 181 | {
|
|---|
| 182 | gettimeofday(¤t_time, NULL);
|
|---|
| 183 | threadparms[i].tnum = i;
|
|---|
| 184 | sprintf(threadparms[i].string, "Thread #%d,time=%d\n",
|
|---|
| 185 | i, current_time.tv_sec);
|
|---|
| 186 | status = pthread_create(&threads[i],
|
|---|
| 187 | (const pthread_attr_t *)NULL,
|
|---|
| 188 | lock_thread,
|
|---|
| 189 | (void *)&threadparms[i]);
|
|---|
| 190 | if ( status != 0 ) {
|
|---|
| 191 | /* Create thread failed - break from loop and quit */
|
|---|
| 192 | printf("main: Create thread # %d failed - terminate\n", i);
|
|---|
| 193 | /* Try to destroy mutex. NOTE: it may fail if another thread */
|
|---|
| 194 | /* is holding the mutex. At this point, we don't care, _BUT_ */
|
|---|
| 195 | /* If this mutex destroy fails and the CPA process is exited with*/
|
|---|
| 196 | /* the mutex still existing, a Synchronization VLOG will most */
|
|---|
| 197 | /* likely be cut indicating this. mutexes should be destroyed by*/
|
|---|
| 198 | /* the application program and not allowed to simply go out of */
|
|---|
| 199 | /* scope and be desctroyed by the system. The system treats this */
|
|---|
| 200 | /* as an abnormal condition */
|
|---|
| 201 | ( void )pthread_mutex_destroy(&mymutex);
|
|---|
| 202 | return(2);
|
|---|
| 203 | } /* endif */
|
|---|
| 204 | } /* endfor */
|
|---|
| 205 |
|
|---|
| 206 | printf("main: sleep until all threads are blocked on mutex\n");
|
|---|
| 207 | sleep(10);
|
|---|
| 208 |
|
|---|
| 209 | gettimeofday(¤t_time, NULL);
|
|---|
| 210 | printf("main: Time before attempting to unlock = %d\n",
|
|---|
| 211 | current_time.tv_sec);
|
|---|
| 212 | status = pthread_mutex_unlock(&mymutex);
|
|---|
| 213 | if ( status != 0 )
|
|---|
| 214 | {
|
|---|
| 215 | /* Mutex unlock failed - break from loop and quit */
|
|---|
| 216 | printf("main: Unlock mutex failed - terminate\n");
|
|---|
| 217 | /* Try to destroy mutex. NOTE: it may fail if another thread */
|
|---|
| 218 | /* is holding the mutex. At this point, we don't care, _BUT_ */
|
|---|
| 219 | /* If this mutex destroy fails and the CPA process is exited with */
|
|---|
| 220 | /* the mutex still existing, a Synchronization VLOG will most */
|
|---|
| 221 | /* likely be cut indicating this. mutexes should be destroyed by */
|
|---|
| 222 | /* the application program and not allowed to simply go out of */
|
|---|
| 223 | /* scope and be desctroyed by the system. The system treats this */
|
|---|
| 224 | /* as an abnormal condition */
|
|---|
| 225 | ( void )pthread_mutex_destroy(&mymutex);
|
|---|
| 226 | printf("main: Testcase failed\n");
|
|---|
| 227 | return(3);
|
|---|
| 228 | } /* endif */
|
|---|
| 229 |
|
|---|
| 230 | for ( i = 0; i < THREAD_COUNT; i++ )
|
|---|
| 231 | {
|
|---|
| 232 | printf("main: Join to child thread %d\n", i+1);
|
|---|
| 233 | status = pthread_join(threads[i], NULL);
|
|---|
| 234 | if ( status!= 0 )
|
|---|
| 235 | {
|
|---|
| 236 | printf("main: Join to Child thread %d failed, errno=%d\n"
|
|---|
| 237 | "main: Testcase failed\n", errno);
|
|---|
| 238 | return(-1);
|
|---|
| 239 | } /* endif */
|
|---|
| 240 | } /* endfor */
|
|---|
| 241 |
|
|---|
| 242 | printf("main: After %d threads performing %d manipulations,\n"
|
|---|
| 243 | "main: the resource is %d\n", THREAD_COUNT,
|
|---|
| 244 | DOIT_COUNT, resource);
|
|---|
| 245 | (void)pthread_mutex_destroy(&mymutex);
|
|---|
| 246 |
|
|---|
| 247 | printf("main: Testcase successful\n");
|
|---|
| 248 | return(0);
|
|---|
| 249 |
|
|---|
| 250 | } /* end */
|
|---|
| 251 |
|
|---|
| 252 |
|
|---|
| 253 | /********************************************************************/
|
|---|
| 254 | /* function: lock_thread */
|
|---|
| 255 | /* */
|
|---|
| 256 | /* description: Thread which locks and unlocks mutex. */
|
|---|
| 257 | /* before accessing the resource that the mutex */
|
|---|
| 258 | /* serializes access to. */
|
|---|
| 259 | /* */
|
|---|
| 260 | /********************************************************************/
|
|---|
| 261 | void *lock_thread(void *parm)
|
|---|
| 262 | {
|
|---|
| 263 | int status; /* return status of APIs */
|
|---|
| 264 | int i; /* loop variable */
|
|---|
| 265 | struct timeval current_time; /* local time value */
|
|---|
| 266 | int tnum = ((threadparmdata_t *)parm)->tnum;
|
|---|
| 267 | /* Note: this parameter assignment doesn't copy the data, it */
|
|---|
| 268 | /* still points to the data assigned up in the main thread and */
|
|---|
| 269 | /* any modifications or deallocation of memory there, will have */
|
|---|
| 270 | /* adverse affects here */
|
|---|
| 271 | char *descript = ((threadparmdata_t *)parm)->string;
|
|---|
| 272 |
|
|---|
| 273 |
|
|---|
| 274 | printf("lt #%d: %s\n", tnum, descript);
|
|---|
| 275 |
|
|---|
| 276 | for ( i=0; i < DOIT_COUNT; ++i ) {
|
|---|
| 277 | gettimeofday(¤t_time, NULL);
|
|---|
| 278 | printf("lt #%d: Current time in secs = %d\n",
|
|---|
| 279 | tnum, current_time.tv_sec);
|
|---|
| 280 |
|
|---|
| 281 | /* get the mutex to serialize access to the resource */
|
|---|
| 282 | /* the threads should not access this resource until the mutex */
|
|---|
| 283 | /* is locked */
|
|---|
| 284 | printf("lt #%d: Lock the mutex\n", tnum);
|
|---|
| 285 | status = pthread_mutex_lock(&mymutex);
|
|---|
| 286 | if (status != 0) {
|
|---|
| 287 | printf("lt #%d: Mutex lock failed = %d\n", tnum, errno);
|
|---|
| 288 | pthread_exit(NULL);
|
|---|
| 289 | }
|
|---|
| 290 |
|
|---|
| 291 | /* Use the process scoped resource in a thread safe manner. */
|
|---|
| 292 | /* this means that we only use it when holding its mutex */
|
|---|
| 293 | gettimeofday(¤t_time, NULL);
|
|---|
| 294 | printf("lt #%d: this thread now holds the mutex to serialize access\n"
|
|---|
| 295 | "lt #%d: to the resource, time = %d\n",
|
|---|
| 296 | tnum, tnum, current_time.tv_sec);
|
|---|
| 297 | /* pretend we're off processing while holding the mutex and */
|
|---|
| 298 | /* this shows how the serialization on one mutex can slow down */
|
|---|
| 299 | /* the processing of the entire job. Really, the mutex should */
|
|---|
| 300 | /* be held only around the access/use of the resource */
|
|---|
| 301 | usleep(1);
|
|---|
| 302 | ++resource;
|
|---|
| 303 | gettimeofday(¤t_time, NULL);
|
|---|
| 304 | printf("lt #%d: Current time before unlock= %d\n",
|
|---|
| 305 | tnum, current_time.tv_sec);
|
|---|
| 306 |
|
|---|
| 307 | /* unlock the mutex, allowing other threads to access the */
|
|---|
| 308 | /* resource. */
|
|---|
| 309 | status = pthread_mutex_unlock(&mymutex);
|
|---|
| 310 | if (status != 0) {
|
|---|
| 311 | printf("lt #%d: Mutex unlock failed = %d\n", tnum, errno);
|
|---|
| 312 | pthread_exit(NULL);
|
|---|
| 313 | }
|
|---|
| 314 | } /* endfor */
|
|---|
| 315 |
|
|---|
| 316 | return(NULL);
|
|---|
| 317 | } /* end */
|
|---|
| 318 |
|
|---|