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