1 /*============================================================================
2   WCSLIB 7.7 - an implementation of the FITS WCS standard.
3   Copyright (C) 1995-2021, Mark Calabretta
4 
5   This file is part of WCSLIB.
6 
7   WCSLIB is free software: you can redistribute it and/or modify it under the
8   terms of the GNU Lesser General Public License as published by the Free
9   Software Foundation, either version 3 of the License, or (at your option)
10   any later version.
11 
12   WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY
13   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14   FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
15   more details.
16 
17   You should have received a copy of the GNU Lesser General Public License
18   along with WCSLIB.  If not, see http://www.gnu.org/licenses.
19 
20   Author: Mark Calabretta, Australia Telescope National Facility, CSIRO.
21   http://www.atnf.csiro.au/people/Mark.Calabretta
22   $Id: tpthreads.c,v 7.7 2021/07/12 06:36:49 mcalabre Exp $
23 *=============================================================================
24 *
25 * tpthreads tests the thread safety of wcspih(), the WCS FITS parser for image
26 * headers.  It closely follows tpih1.c, launching multiple threads in rapid
27 * succession to read a test header.
28 *
29 * Input comes from file "pih.fits" read directly using fgets().
30 *
31 *---------------------------------------------------------------------------*/
32 
33 #include <wcsconfig_tests.h>
34 
35 #include<pthread.h>
36 #include<stdio.h>
37 #include<string.h>
38 #include<unistd.h>
39 
40 #include <wcshdr.h>
41 
42 #define NTHREAD 8
43 
44 pthread_t threadId[NTHREAD];
45 
46 void *threadFn(void *threadarg);
47 
48 struct threadArg {
49   char *header;
50   int  nkeyrec;
51   int  relax;
52   int  ctrl;
53 };
54 
55 struct threadRet {
56   int status;
57   int nreject;
58   int nwcs;
59   struct wcsprm *wcs;
60 } threadret[NTHREAD];
61 
62 
main(void)63 int main(void)
64 {
65   char infile[] = "pih.fits";
66   int nkeyrec = 0;
67 
68   // Set for unbuffered output so messages are printed immediately.
69   setvbuf(stdout, NULL, _IONBF, 0);
70 
71   printf("Testing WCSLIB header parser for thread safety (tpthreads.c)\n"
72          "------------------------------------------------------------\n\n");
73 
74   // Read in the FITS header, excluding COMMENT and HISTORY keyrecords.
75   FILE *fptr;
76   if ((fptr = fopen(infile, "r")) == 0) {
77     fprintf(stderr, "ERROR opening %s\n", infile);
78     return 1;
79   }
80 
81   char keyrec[81], header[288001];
82   int k = 0;
83   int gotend = 0;
84   for (int iblock = 0; iblock < 100; iblock++) {
85     for (int ikeyrec = 0; ikeyrec < 36; ikeyrec++) {
86       if (fgets(keyrec, 81, fptr) == 0) {
87         break;
88       }
89 
90       // Cull COMMENT and HISTORY keyrecords.
91       if (strncmp(keyrec, "        ", 8) == 0) continue;
92       if (strncmp(keyrec, "COMMENT ", 8) == 0) continue;
93       if (strncmp(keyrec, "HISTORY ", 8) == 0) continue;
94 
95       memcpy(header+k, keyrec, 80);
96       k += 80;
97       nkeyrec++;
98 
99       if (strncmp(keyrec, "END     ", 8) == 0) {
100         // An END keyrecord was read, but read the rest of the block.
101         gotend = 1;
102       }
103     }
104 
105     if (gotend) break;
106   }
107   fclose(fptr);
108 
109   fprintf(stderr, "Found %d non-comment header keyrecords.\n\n", nkeyrec);
110 
111   // The threadArg struct contains constant data shared by all threads.
112   struct threadArg threadarg = {header, nkeyrec, WCSHDR_all, 0};
113   for (int iloop = 0; iloop < 10; iloop++) {
114     printf("\nPass %d:\n", iloop);
115 
116     // Launch multiple threads parsing the same header.
117     for (int ithread = 0; ithread < NTHREAD; ithread++) {
118       printf("Thread %d...\n", ithread);
119 
120       int status;
121       if ((status = pthread_create(&(threadId[ithread]), NULL, &threadFn,
122                                    &threadarg))) {
123         printf("Failed to create thread %d: %s", ithread, strerror(status));
124       } else {
125         printf("Thread %d created successfully.\n", ithread);
126       }
127     }
128 
129     // Wait for each thread to finish.
130     for (int ithread = 0; ithread < NTHREAD; ithread++) {
131       struct threadRet *ret;
132       pthread_join(threadId[ithread], (void**)&ret);
133       printf("Thread %d status return: %d.\n", ithread, ret->status);
134 
135       // Free memory allocated by wcspih().
136       wcsvfree(&ret->nwcs, &ret->wcs);
137     }
138   }
139 
140   return 0;
141 }
142 
143 
144 /*----------------------------------------------------------------------------
145 * Invoke wcspih() in a thread.
146 ----------------------------------------------------------------------------*/
147 
threadFn(void * threadarg)148 void *threadFn(void *threadarg)
149 {
150   // Determine our thread index.
151   pthread_t selfId = pthread_self();
152   int ithread;
153   for (ithread = 0; ithread < NTHREAD; ithread++) {
154     if (pthread_equal(selfId, threadId[ithread])) {
155       break;
156     }
157   }
158 
159   printf("Thread %d processing started.\n", ithread);
160 
161   // Invoke wcspih().
162   struct threadArg *arg = (struct threadArg *)threadarg;
163   struct threadRet *ret = threadret + ithread;
164   ret->status = wcspih(arg->header, arg->nkeyrec, arg->relax, arg->ctrl,
165                        &ret->nreject, &ret->nwcs, &ret->wcs);
166 
167   // Terminate the thread.
168   printf("Thread %d processing finished.\n", ithread);
169   pthread_exit(ret);
170 
171   return NULL;
172 }
173