1 /* Very efficient antebuffer to preempt the file(s) load.
2    Syntax: antebuffer X file [file] | YourCommand
3    with buffer size = 2^X.
4    The files are written on stdout.
5    Best size for the buffer is about 4MB (local disk) to
6    16MB (NFS), eventually until 128 MB.
7    Limitation: X <= 31; more than 2GB antebuffer has no sense anyway.
8 */
9 
10 /* To avoid the warning: implicit declaration of nanosleep for c99 compliant */
11 #include "cado.h" // IWYU pragma: keep
12 #ifdef _POSIX_C_SOURCE
13 #undef _POSIX_C_SOURCE
14 #endif
15 #define _POSIX_C_SOURCE 200809L
16 
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <stdint.h>
23 #include <pthread.h>
24 #include <errno.h>
25 #include <time.h>
26 #include "macros.h"     // LIKELY UNLIKELY
27 #include "timing.h"
28 #include "portability.h" // sleep // IWYU pragma: keep
29 
30 #ifdef HAVE_MINGW
31 int _CRT_fmode = _O_BINARY; /* Binary open for stdin/out/err */
32 #endif
33 
34 #ifndef HAVE_NANOSLEEP
nanosleep(const struct timespec * req,struct timespec * rem)35   int nanosleep(const struct timespec *req, struct timespec *rem) {
36     if (rem == NULL) {
37       /* Dummy to shut up the warning */
38     }
39 #ifdef HAVE_USLEEP
40     unsigned long usec = req->tv_sec * 1000000UL + req->tv_nsec / 1000UL;
41     usleep(usec);
42 #else
43     sleep(req->tv_sec);
44 #endif
45     return 0;
46   }
47 #endif
48 
49 /* These variables are used by pthread and main */
50 static volatile uintptr_t ab_cptp = 0, ab_cptc = 0;        /* Main counters */
51 static volatile int ab_end = 0;                            /* 1 if end */
52 static char *ab_buf;                                       /* Main buffer */
53 static const struct timespec waiting = { 0, 1<<13 };       /* About 8 to 20 microseconds */
54 static int ab_in;                                          /* fd for loading */
55 static size_t ab_size, ab_sizeio;                          /* size for buffer & in */
56 
57 /* The 3 shared variables must be considered with non atomical access.
58    So, dedicated mutex protection for all.
59    NB: spinlocks are not mandatory in POSIX and don't exist in macos.
60    But it's so uncommon a mutex is blocking here than the mutex version
61    has the same speed than the spin locks version, and both are faster
62    than the gcc __builtin_fetch_and_add version.
63 */
64 static pthread_mutex_t mutex_ab_cptp = PTHREAD_MUTEX_INITIALIZER;
65 static pthread_mutex_t mutex_ab_cptc = PTHREAD_MUTEX_INITIALIZER;
66 static pthread_mutex_t mutex_ab_end = PTHREAD_MUTEX_INITIALIZER;
67 
ab_cons()68 static void ab_cons () {
69   uintptr_t cpy_ab_cptc = 0;
70   size_t c, t = 0;
71   ssize_t w;
72 
73   pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, NULL);
74   for ( ; ; ) {
75     for ( ; ; ) {
76       uintptr_t mut_ab_cptp;
77       int mut_ab_end;
78       pthread_mutex_lock (&mutex_ab_cptp); mut_ab_cptp = ab_cptp; pthread_mutex_unlock (&mutex_ab_cptp);
79       c = (mut_ab_cptp - cpy_ab_cptc);
80       if (LIKELY (c)) break;
81       pthread_mutex_lock (&mutex_ab_end); mut_ab_end = ab_end; pthread_mutex_unlock (&mutex_ab_end);
82       if (UNLIKELY (mut_ab_end)) {
83 	pthread_mutex_lock (&mutex_ab_cptp); mut_ab_cptp = ab_cptp; pthread_mutex_unlock (&mutex_ab_cptp);
84 	c = (int) (mut_ab_cptp - cpy_ab_cptc);
85 	if (LIKELY (c)) break;
86 	pthread_exit(NULL);
87       }
88       nanosleep (&waiting, NULL);
89     }
90     if (c > ab_sizeio) c = ab_sizeio;
91     if (c > ab_size - t) c = ab_size - t;
92     while ((w = write(1, &(ab_buf[t]), c)) <= 0) nanosleep (&waiting, NULL);
93     cpy_ab_cptc += w;
94     t = (t + w) & (ab_size - 1);
95     pthread_mutex_lock (&mutex_ab_cptc); ab_cptc = cpy_ab_cptc; pthread_mutex_unlock (&mutex_ab_cptc);
96   }
97 }
98 
main(int argc,char ** argv)99 int main(int argc, char **argv) {
100   pthread_t ab_tc;
101   pthread_attr_t ab_attr;
102   uintptr_t cpy_ab_cptp = 0;
103   size_t c, t = 0;
104   ssize_t r;
105   unsigned int p;
106   char *real_malloc;
107 
108 #ifdef HAVE_MINGW
109   _fmode = _O_BINARY;     /* Binary open for all others files */
110 #endif
111 
112   if (argc < 3) {
113   error:
114     fprintf (stderr, "%s syntax:  %s SIZE file [file]  or  %s SIZE -\n"
115 	     "This command is an ante buffer which loads the files or stdin in a\n"
116 	     "buffer and write them on stdout. The size of this buffer is 2^SIZE.\n"
117 	     "16<=SIZE<=31, best for 20 to 24.\n", argv[0], argv[0], argv[0]);
118     exit (1);
119   }
120   if ((sscanf(argv[1], "%zu", &ab_size)) != 1) goto error;
121   if (ab_size < 16 || ab_size > 31) goto error;
122   ab_size = (uintptr_t) 1 << ab_size;
123   ab_sizeio = ab_size >> 4;
124   if (!(real_malloc = malloc (ab_size + 0xFFF))) {
125     fprintf (stderr, "%s: malloc error: %s\n", argv[0], strerror(errno));
126     exit(1);
127   }
128   ab_buf = (char *) (((uintptr_t) real_malloc + 0xFFF) & ~ (uintptr_t) 0xFFF);
129   pthread_attr_init(&ab_attr);
130   pthread_attr_setstacksize(&ab_attr, 1<<12);
131   pthread_attr_setdetachstate(&ab_attr, PTHREAD_CREATE_JOINABLE);
132   if (pthread_create(&ab_tc, &ab_attr, (void *) ab_cons, NULL)) {
133     fprintf (stderr, "%s: pthread_create error: %s\n", argv[0], strerror(errno));
134     exit(1);
135   }
136   if (strcmp(argv[2], "-")) {
137     p = 3;
138     if ((ab_in = open(argv[2], O_RDONLY)) == -1) {
139       fprintf (stderr, "%s: open or load error in file %s: %s\n", argv[0], argv[2], strerror(errno));
140       exit (1);
141     }
142   }
143   else {
144     p = 0;
145     ab_in = 0;
146   }
147   for ( ; ; ) {
148     for ( ; ; ) {
149       uintptr_t mut_ab_cptc;
150       pthread_mutex_lock (&mutex_ab_cptc); mut_ab_cptc = ab_cptc; pthread_mutex_unlock (&mutex_ab_cptc);
151       c = ((mut_ab_cptc + ab_size) - cpy_ab_cptp);
152       if (LIKELY (c)) break;
153       nanosleep (&waiting, NULL);
154     }
155     if (c > ab_sizeio)   c = ab_sizeio;
156     if (c > ab_size - t) c = ab_size - t;
157     r = read(ab_in, &(ab_buf[t]), c);
158     if (r > 0) {
159       cpy_ab_cptp += r;
160       t = (t + r) & (ab_size - 1);
161       pthread_mutex_lock (&mutex_ab_cptp); ab_cptp = cpy_ab_cptp; pthread_mutex_unlock (&mutex_ab_cptp);
162     }
163     else
164       if (!r) {
165 	if (!p || p == (unsigned int) argc) {
166 	  pthread_mutex_lock (&mutex_ab_end); ab_end = 1; pthread_mutex_unlock (&mutex_ab_end);
167 	  break;
168 	}
169 	close(ab_in);
170 	if ((ab_in = open(argv[p++], O_RDONLY)) == -1) {
171 	  fprintf (stderr, "%s: open or load error in file %s: %s\n", argv[0], argv[p-1], strerror(errno));
172 	  exit (1);
173 	}
174       }
175       else {
176 	fprintf (stderr, "%s: read error in file %s: %s\n", argv[0], argv[p-1], strerror(errno));
177 	  exit (1);
178 	}
179   }
180   if (p) close(ab_in);
181   pthread_join(ab_tc, NULL);
182   pthread_mutex_destroy (&mutex_ab_end);
183   pthread_mutex_destroy (&mutex_ab_cptc);
184   pthread_mutex_destroy (&mutex_ab_cptp);
185   free (real_malloc);
186   double tt[2];
187   seconds_user_sys(tt);
188   /*
189   fprintf(stderr, "antebuffer exits after having spent %.2fs+%.2fs on cpu\n",
190           tt[0], tt[1]);
191           */
192   exit (0);
193 }
194