1 /*
2 * simple multi-thread stress test for PCM
3 *
4 * The main thread simply feeds or reads the sample data with the
5 * random size continuously. Meanwhile, the worker threads call some
6 * update function depending on the given mode, and show the thread
7 * number of the read value.
8 *
9 * The function for the worker thread is specified via -m option.
10 * When the random mode ('r') is set, the update function is chosen
11 * randomly in the loop.
12 *
13 * When the -v option is passed, this tries to show some obtained value
14 * from the function. Without -v, as default, it shows the thread number
15 * (0-9). In addition, it puts the mode suffix ('a' for avail, 'd' for
16 * delay, etc) for the random mode, as well as the suffix '!' indicating
17 * the error from the called function.
18 */
19
20 #include <stdio.h>
21 #include <pthread.h>
22 #include <getopt.h>
23 #include "../include/asoundlib.h"
24
25 #define MAX_THREADS 10
26
27 enum {
28 MODE_AVAIL_UPDATE,
29 MODE_STATUS,
30 MODE_HWSYNC,
31 MODE_TIMESTAMP,
32 MODE_DELAY,
33 MODE_RANDOM
34 };
35
36 static char mode_suffix[] = {
37 'a', 's', 'h', 't', 'd', 'r'
38 };
39
40 static const char *devname = "default";
41 static int stream = SND_PCM_STREAM_PLAYBACK;
42 static int num_threads = 1;
43 static int periodsize = 16 * 1024;
44 static int bufsize = 16 * 1024 * 4;
45 static int channels = 2;
46 static int rate = 48000;
47 static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
48
49 static int running_mode = MODE_AVAIL_UPDATE;
50 static int show_value = 0;
51 static int quiet = 0;
52
53 static pthread_t peeper_threads[MAX_THREADS];
54 static int running = 1;
55 static snd_pcm_t *pcm;
56
peeper(void * data)57 static void *peeper(void *data)
58 {
59 int thread_no = (long)data;
60 snd_pcm_sframes_t val;
61 snd_pcm_status_t *stat;
62 snd_htimestamp_t tstamp;
63 int mode = running_mode, err;
64
65 snd_pcm_status_alloca(&stat);
66
67 while (running) {
68 if (running_mode == MODE_RANDOM)
69 mode = rand() % MODE_RANDOM;
70 switch (mode) {
71 case MODE_AVAIL_UPDATE:
72 val = snd_pcm_avail_update(pcm);
73 err = 0;
74 break;
75 case MODE_STATUS:
76 err = snd_pcm_status(pcm, stat);
77 val = snd_pcm_status_get_avail(stat);
78 break;
79 case MODE_HWSYNC:
80 err = snd_pcm_hwsync(pcm);
81 break;
82 case MODE_TIMESTAMP:
83 err = snd_pcm_htimestamp(pcm, (snd_pcm_uframes_t *)&val,
84 &tstamp);
85 break;
86 default:
87 err = snd_pcm_delay(pcm, &val);
88 break;
89 }
90
91 if (quiet)
92 continue;
93 if (running_mode == MODE_RANDOM) {
94 fprintf(stderr, "%d%c%s", thread_no, mode_suffix[mode],
95 err ? "!" : "");
96 } else {
97 if (show_value && mode != MODE_HWSYNC)
98 fprintf(stderr, "\r%d ", (int)val);
99 else
100 fprintf(stderr, "%d%s", thread_no,
101 err ? "!" : "");
102 }
103 }
104 return NULL;
105 }
106
usage(void)107 static void usage(void)
108 {
109 fprintf(stderr, "usage: multi-thread [-options]\n");
110 fprintf(stderr, " -D str Set device name\n");
111 fprintf(stderr, " -r val Set sample rate\n");
112 fprintf(stderr, " -p val Set period size (in frame)\n");
113 fprintf(stderr, " -b val Set buffer size (in frame)\n");
114 fprintf(stderr, " -c val Set number of channels\n");
115 fprintf(stderr, " -f str Set PCM format\n");
116 fprintf(stderr, " -s str Set stream direction (playback or capture)\n");
117 fprintf(stderr, " -t val Set number of threads\n");
118 fprintf(stderr, " -m str Running mode (avail, status, hwsync, timestamp, delay, random)\n");
119 fprintf(stderr, " -v Show value\n");
120 fprintf(stderr, " -q Quiet mode\n");
121 }
122
parse_options(int argc,char ** argv)123 static int parse_options(int argc, char **argv)
124 {
125 int c, i;
126
127 while ((c = getopt(argc, argv, "D:r:f:p:b:s:t:m:vq")) >= 0) {
128 switch (c) {
129 case 'D':
130 devname = optarg;
131 break;
132 case 'r':
133 rate = atoi(optarg);
134 break;
135 case 'p':
136 periodsize = atoi(optarg);
137 break;
138 case 'b':
139 bufsize = atoi(optarg);
140 break;
141 case 'c':
142 channels = atoi(optarg);
143 break;
144 case 'f':
145 format = snd_pcm_format_value(optarg);
146 break;
147 case 's':
148 if (*optarg == 'p' || *optarg == 'P')
149 stream = SND_PCM_STREAM_PLAYBACK;
150 else if (*optarg == 'c' || *optarg == 'C')
151 stream = SND_PCM_STREAM_CAPTURE;
152 else {
153 fprintf(stderr, "invalid stream direction\n");
154 return 1;
155 }
156 break;
157 case 't':
158 num_threads = atoi(optarg);
159 if (num_threads < 1 || num_threads > MAX_THREADS) {
160 fprintf(stderr, "invalid number of threads\n");
161 return 1;
162 }
163 break;
164 case 'm':
165 for (i = 0; i <= MODE_RANDOM; i++)
166 if (mode_suffix[i] == *optarg)
167 break;
168 if (i > MODE_RANDOM) {
169 fprintf(stderr, "invalid mode type\n");
170 return 1;
171 }
172 running_mode = i;
173 break;
174 case 'v':
175 show_value = 1;
176 break;
177 case 'q':
178 quiet = 1;
179 break;
180 default:
181 usage();
182 return 1;
183 }
184 }
185 return 0;
186 }
187
setup_params(void)188 static int setup_params(void)
189 {
190 snd_pcm_hw_params_t *hw;
191
192 /* FIXME: more finer error checks */
193 snd_pcm_hw_params_alloca(&hw);
194 snd_pcm_hw_params_any(pcm, hw);
195 snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED);
196 snd_pcm_hw_params_set_format(pcm, hw, format);
197 snd_pcm_hw_params_set_channels(pcm, hw, channels);
198 snd_pcm_hw_params_set_rate(pcm, hw, rate, 0);
199 snd_pcm_hw_params_set_period_size(pcm, hw, periodsize, 0);
200 snd_pcm_hw_params_set_buffer_size(pcm, hw, bufsize);
201 if (snd_pcm_hw_params(pcm, hw) < 0) {
202 fprintf(stderr, "snd_pcm_hw_params error\n");
203 return 1;
204 }
205 return 0;
206 }
207
main(int argc,char ** argv)208 int main(int argc, char **argv)
209 {
210 char *buf;
211 int i, err;
212
213 if (parse_options(argc, argv))
214 return 1;
215
216 err = snd_pcm_open(&pcm, devname, stream, 0);
217 if (err < 0) {
218 fprintf(stderr, "cannot open pcm %s\n", devname);
219 return 1;
220 }
221
222 if (setup_params())
223 return 1;
224
225 buf = calloc(1, snd_pcm_format_size(format, bufsize) * channels);
226 if (!buf) {
227 fprintf(stderr, "cannot alloc buffer\n");
228 return 1;
229 }
230
231 for (i = 0; i < num_threads; i++) {
232 if (pthread_create(&peeper_threads[i], NULL, peeper, (void *)(long)i)) {
233 fprintf(stderr, "pthread_create error\n");
234 return 1;
235 }
236 }
237
238 if (stream == SND_PCM_STREAM_CAPTURE)
239 snd_pcm_start(pcm);
240 for (;;) {
241 int size = rand() % (bufsize / 2);
242 if (stream == SND_PCM_STREAM_PLAYBACK)
243 err = snd_pcm_writei(pcm, buf, size);
244 else
245 err = snd_pcm_readi(pcm, buf, size);
246 if (err < 0) {
247 fprintf(stderr, "read/write error %d\n", err);
248 err = snd_pcm_recover(pcm, err, 0);
249 if (err < 0)
250 break;
251 if (stream == SND_PCM_STREAM_CAPTURE)
252 snd_pcm_start(pcm);
253 }
254 }
255
256 running = 0;
257 for (i = 0; i < num_threads; i++)
258 pthread_cancel(peeper_threads[i]);
259 for (i = 0; i < num_threads; i++)
260 pthread_join(peeper_threads[i], NULL);
261
262 return 1;
263 }
264