GLib: implement GMutex natively on Linux
[platform/upstream/glib.git] / glib / tests / spawn-multithreaded.c
1 /* 
2  * Copyright (C) 2011 Red Hat, Inc.
3  *
4  * This work is provided "as is"; redistribution and modification
5  * in whole or in part, in any medium, physical or electronic is
6  * permitted without restriction.
7  *
8  * This work is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *
12  * In no event shall the authors or contributors be liable for any
13  * direct, indirect, incidental, special, exemplary, or consequential
14  * damages (including, but not limited to, procurement of substitute
15  * goods or services; loss of use, data, or profits; or business
16  * interruption) however caused and on any theory of liability, whether
17  * in contract, strict liability, or tort (including negligence or
18  * otherwise) arising in any way out of the use of this software, even
19  * if advised of the possibility of such damage.
20  *
21  * Author: Colin Walters <walters@verbum.org> 
22  */
23
24 #include "config.h"
25
26 #include <glib.h>
27 #include <string.h>
28
29 static char *echo_prog_path;
30
31 static void
32 multithreaded_test_run (GThreadFunc function)
33 {
34   int i;
35   GPtrArray *threads = g_ptr_array_new ();
36   guint n_threads;
37
38   /* Limit to 64, otherwise we may hit file descriptor limits and such */
39   n_threads = MIN (g_get_num_processors () * 2, 64);
40
41   for (i = 0; i < n_threads; i++)
42     {
43       GThread *thread;
44
45       thread = g_thread_new ("test", function, GINT_TO_POINTER (i));
46       g_ptr_array_add (threads, thread);
47     }
48
49   for (i = 0; i < n_threads; i++)
50     {
51       gpointer ret;
52       ret = g_thread_join (g_ptr_array_index (threads, i));
53       g_assert_cmpint (GPOINTER_TO_INT (ret), ==, i);
54     }
55   g_ptr_array_free (threads, TRUE);
56 }
57
58 static gpointer
59 test_spawn_sync_multithreaded_instance (gpointer data)
60 {
61   int tnum = GPOINTER_TO_INT (data);
62   GError *error = NULL;
63   GPtrArray *argv;
64   char *arg;
65   char *stdout_str;
66   int estatus;
67
68   arg = g_strdup_printf ("thread %d", tnum);
69
70   argv = g_ptr_array_new ();
71   g_ptr_array_add (argv, echo_prog_path);
72   g_ptr_array_add (argv, arg);
73   g_ptr_array_add (argv, NULL);
74
75   g_spawn_sync (NULL, (char**)argv->pdata, NULL, G_SPAWN_DEFAULT, NULL, NULL, &stdout_str, NULL, &estatus, &error);
76   g_assert_no_error (error);
77   g_assert_cmpstr (arg, ==, stdout_str);
78   g_free (arg);
79   g_free (stdout_str);
80   g_ptr_array_free (argv, TRUE);
81
82   return GINT_TO_POINTER (tnum);
83 }
84
85 static void
86 test_spawn_sync_multithreaded (void)
87 {
88   multithreaded_test_run (test_spawn_sync_multithreaded_instance);
89 }
90
91 typedef struct {
92   GMainLoop *loop;
93   gboolean child_exited;
94   gboolean stdout_done;
95   GString *stdout_buf;
96 } SpawnAsyncMultithreadedData;
97
98 static gboolean
99 on_child_exited (GPid     pid,
100                  gint     status,
101                  gpointer datap)
102 {
103   SpawnAsyncMultithreadedData *data = datap;
104
105   data->child_exited = TRUE;
106   if (data->child_exited && data->stdout_done)
107     g_main_loop_quit (data->loop);
108   
109   return G_SOURCE_REMOVE;
110 }
111
112 static gboolean
113 on_child_stdout (GIOChannel   *channel,
114                  GIOCondition  condition,
115                  gpointer      datap)
116 {
117   char buf[1024];
118   GError *error = NULL;
119   gsize bytes_read;
120   GIOStatus status;
121   SpawnAsyncMultithreadedData *data = datap;
122
123  read:
124   status = g_io_channel_read_chars (channel, buf, sizeof (buf), &bytes_read, &error);
125   if (status == G_IO_STATUS_NORMAL)
126     {
127       g_string_append_len (data->stdout_buf, buf, (gssize) bytes_read);
128       if (bytes_read == sizeof (buf))
129         goto read;
130     }
131   else if (status == G_IO_STATUS_EOF)
132     {
133       g_string_append_len (data->stdout_buf, buf, (gssize) bytes_read);
134       data->stdout_done = TRUE;
135     }
136   else if (status == G_IO_STATUS_ERROR)
137     {
138       g_error ("Error reading from child stdin: %s", error->message);
139     }
140
141   if (data->child_exited && data->stdout_done)
142     g_main_loop_quit (data->loop);
143
144   return !data->stdout_done;
145 }
146
147 static gpointer
148 test_spawn_async_multithreaded_instance (gpointer thread_data)
149 {
150   int tnum = GPOINTER_TO_INT (thread_data);
151   GError *error = NULL;
152   GPtrArray *argv;
153   char *arg;
154   GPid pid;
155   GMainContext *context;
156   GMainLoop *loop;
157   GIOChannel *channel;
158   GSource *source;
159   int child_stdout_fd;
160   SpawnAsyncMultithreadedData data;
161
162   context = g_main_context_new ();
163   loop = g_main_loop_new (context, TRUE);
164
165   arg = g_strdup_printf ("thread %d", tnum);
166
167   argv = g_ptr_array_new ();
168   g_ptr_array_add (argv, echo_prog_path);
169   g_ptr_array_add (argv, arg);
170   g_ptr_array_add (argv, NULL);
171
172   g_spawn_async_with_pipes (NULL, (char**)argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL,
173                             &child_stdout_fd, NULL, &error);
174   g_assert_no_error (error);
175   g_ptr_array_free (argv, TRUE);
176
177   data.loop = loop;
178   data.stdout_done = FALSE;
179   data.child_exited = FALSE;
180   data.stdout_buf = g_string_new (0);
181
182   source = g_child_watch_source_new (pid);
183   g_source_set_callback (source, (GSourceFunc)on_child_exited, &data, NULL);
184   g_source_attach (source, context);
185   g_source_unref (source);
186
187   channel = g_io_channel_unix_new (child_stdout_fd);
188   source = g_io_create_watch (channel, G_IO_IN | G_IO_HUP);
189   g_source_set_callback (source, (GSourceFunc)on_child_stdout, &data, NULL);
190   g_source_attach (source, context);
191   g_source_unref (source);
192
193   g_main_loop_run (loop);
194
195   g_assert (data.child_exited);
196   g_assert (data.stdout_done);
197   g_assert_cmpstr (data.stdout_buf->str, ==, arg);
198   g_string_free (data.stdout_buf, TRUE);
199
200   g_io_channel_unref (channel);
201   g_main_context_unref (context);
202   g_main_loop_unref (loop);
203
204   g_free (arg);
205
206   return GINT_TO_POINTER (tnum);
207 }
208
209 static void
210 test_spawn_async_multithreaded (void)
211 {
212   multithreaded_test_run (test_spawn_async_multithreaded_instance);
213 }
214
215 int
216 main (int   argc,
217       char *argv[])
218 {
219   char *dirname;
220   int ret;
221
222   g_test_init (&argc, &argv, NULL);
223
224   dirname = g_path_get_dirname (argv[0]);
225   echo_prog_path = g_build_filename (dirname, "test-spawn-echo" EXEEXT, NULL);
226   if (!g_file_test (echo_prog_path, G_FILE_TEST_EXISTS))
227     {
228       g_free (echo_prog_path);
229       echo_prog_path = g_build_filename (dirname, "lt-test-spawn-echo" EXEEXT, NULL);
230     }
231   g_free (dirname);
232
233   g_assert (g_file_test (echo_prog_path, G_FILE_TEST_EXISTS));
234
235   g_test_add_func ("/gthread/spawn-sync", test_spawn_sync_multithreaded);
236   g_test_add_func ("/gthread/spawn-async", test_spawn_async_multithreaded);
237
238   ret = g_test_run();
239
240   g_free (echo_prog_path);
241
242   return ret;
243 }