1 | /* GDK - The GIMP Drawing Kit |
2 | * |
3 | * Copyright (C) 2017 Benjamin Otte <otte@gnome.org> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
17 | */ |
18 | |
19 | #include "config.h" |
20 | |
21 | #include "gdkpipeiostreamprivate.h" |
22 | |
23 | #include <string.h> |
24 | |
25 | /* PIPE */ |
26 | |
27 | typedef enum { |
28 | GDK_IO_PIPE_EMPTY, |
29 | GDK_IO_PIPE_INPUT_BUFFER, |
30 | GDK_IO_PIPE_OUTPUT_BUFFER |
31 | } GdkIOPipeState; |
32 | |
33 | typedef struct _GdkIOPipe GdkIOPipe; |
34 | |
35 | struct _GdkIOPipe |
36 | { |
37 | int ref_count; |
38 | |
39 | GMutex mutex; |
40 | GCond cond; |
41 | guchar *buffer; |
42 | gsize size; |
43 | GdkIOPipeState state : 2; |
44 | guint input_closed : 1; |
45 | guint output_closed : 1; |
46 | }; |
47 | |
48 | static GdkIOPipe * |
49 | gdk_io_pipe_new (void) |
50 | { |
51 | GdkIOPipe *pipe; |
52 | |
53 | pipe = g_slice_new0 (GdkIOPipe); |
54 | pipe->ref_count = 1; |
55 | |
56 | g_mutex_init (mutex: &pipe->mutex); |
57 | g_cond_init (cond: &pipe->cond); |
58 | |
59 | return pipe; |
60 | } |
61 | |
62 | static GdkIOPipe * |
63 | gdk_io_pipe_ref (GdkIOPipe *pipe) |
64 | { |
65 | g_atomic_int_inc (&pipe->ref_count); |
66 | |
67 | return pipe; |
68 | } |
69 | |
70 | static void |
71 | gdk_io_pipe_unref (GdkIOPipe *pipe) |
72 | { |
73 | if (!g_atomic_int_dec_and_test (&pipe->ref_count)) |
74 | return; |
75 | |
76 | g_cond_clear (cond: &pipe->cond); |
77 | g_mutex_clear (mutex: &pipe->mutex); |
78 | } |
79 | |
80 | static void |
81 | gdk_io_pipe_lock (GdkIOPipe *pipe) |
82 | { |
83 | g_mutex_lock (mutex: &pipe->mutex); |
84 | } |
85 | |
86 | static void |
87 | gdk_io_pipe_unlock (GdkIOPipe *pipe) |
88 | { |
89 | g_mutex_unlock (mutex: &pipe->mutex); |
90 | } |
91 | |
92 | /* INPUT STREAM */ |
93 | |
94 | #define GDK_TYPE_PIPE_INPUT_STREAM (gdk_pipe_input_stream_get_type ()) |
95 | #define GDK_PIPE_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_PIPE_INPUT_STREAM, GdkPipeInputStream)) |
96 | #define GDK_IS_PIPE_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_PIPE_INPUT_STREAM)) |
97 | #define GDK_PIPE_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIPE_INPUT_STREAM, GdkPipeInputStreamClass)) |
98 | #define GDK_IS_PIPE_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIPE_INPUT_STREAM)) |
99 | #define GDK_PIPE_INPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIPE_INPUT_STREAM, GdkPipeInputStreamClass)) |
100 | |
101 | typedef struct _GdkPipeInputStream GdkPipeInputStream; |
102 | typedef struct _GdkPipeInputStreamClass GdkPipeInputStreamClass; |
103 | |
104 | struct _GdkPipeInputStream |
105 | { |
106 | GInputStream parent; |
107 | |
108 | GdkIOPipe *pipe; |
109 | }; |
110 | |
111 | struct _GdkPipeInputStreamClass |
112 | { |
113 | GInputStreamClass parent_class; |
114 | }; |
115 | |
116 | GType gdk_pipe_input_stream_get_type (void) G_GNUC_CONST; |
117 | |
118 | G_DEFINE_TYPE (GdkPipeInputStream, gdk_pipe_input_stream, G_TYPE_INPUT_STREAM) |
119 | |
120 | static void |
121 | gdk_pipe_input_stream_finalize (GObject *object) |
122 | { |
123 | GdkPipeInputStream *pipe = GDK_PIPE_INPUT_STREAM (object); |
124 | |
125 | g_clear_pointer (&pipe->pipe, gdk_io_pipe_unref); |
126 | |
127 | G_OBJECT_CLASS (gdk_pipe_input_stream_parent_class)->finalize (object); |
128 | } |
129 | |
130 | static gssize |
131 | gdk_pipe_input_stream_read (GInputStream *stream, |
132 | void *buffer, |
133 | gsize count, |
134 | GCancellable *cancellable, |
135 | GError **error) |
136 | { |
137 | GdkPipeInputStream *pipe_stream = GDK_PIPE_INPUT_STREAM (stream); |
138 | GdkIOPipe *pipe = pipe_stream->pipe; |
139 | gsize amount; |
140 | |
141 | gdk_io_pipe_lock (pipe); |
142 | |
143 | switch (pipe->state) |
144 | { |
145 | case GDK_IO_PIPE_EMPTY: |
146 | if (pipe->output_closed) |
147 | { |
148 | amount = 0; |
149 | break; |
150 | } |
151 | pipe->buffer = buffer; |
152 | pipe->size = count; |
153 | pipe->state = GDK_IO_PIPE_INPUT_BUFFER; |
154 | do |
155 | g_cond_wait (cond: &pipe->cond, mutex: &pipe->mutex); |
156 | while (pipe->size == count && |
157 | pipe->state == GDK_IO_PIPE_INPUT_BUFFER && |
158 | !pipe->output_closed); |
159 | if (pipe->state == GDK_IO_PIPE_INPUT_BUFFER) |
160 | { |
161 | amount = count - pipe->size; |
162 | pipe->state = GDK_IO_PIPE_EMPTY; |
163 | pipe->size = 0; |
164 | } |
165 | else |
166 | { |
167 | amount = count; |
168 | } |
169 | break; |
170 | |
171 | case GDK_IO_PIPE_OUTPUT_BUFFER: |
172 | amount = MIN (count, pipe->size); |
173 | |
174 | memcpy (dest: buffer, src: pipe->buffer, n: amount); |
175 | pipe->size -= amount; |
176 | |
177 | if (pipe->size == 0) |
178 | pipe->state = GDK_IO_PIPE_EMPTY; |
179 | else |
180 | pipe->buffer += amount; |
181 | break; |
182 | |
183 | case GDK_IO_PIPE_INPUT_BUFFER: |
184 | default: |
185 | g_assert_not_reached (); |
186 | amount = 0; |
187 | break; |
188 | } |
189 | |
190 | g_cond_broadcast (cond: &pipe->cond); |
191 | gdk_io_pipe_unlock (pipe); |
192 | |
193 | return amount; |
194 | } |
195 | |
196 | static gboolean |
197 | gdk_pipe_input_stream_close (GInputStream *stream, |
198 | GCancellable *cancellable, |
199 | GError **error) |
200 | { |
201 | GdkPipeInputStream *pipe_stream = GDK_PIPE_INPUT_STREAM (stream); |
202 | GdkIOPipe *pipe = pipe_stream->pipe; |
203 | |
204 | gdk_io_pipe_lock (pipe); |
205 | |
206 | pipe->input_closed = TRUE; |
207 | g_cond_broadcast (cond: &pipe->cond); |
208 | |
209 | gdk_io_pipe_unlock (pipe); |
210 | |
211 | return TRUE; |
212 | } |
213 | |
214 | static void |
215 | gdk_pipe_input_stream_class_init (GdkPipeInputStreamClass *class) |
216 | { |
217 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
218 | GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (class); |
219 | |
220 | object_class->finalize = gdk_pipe_input_stream_finalize; |
221 | |
222 | input_stream_class->read_fn = gdk_pipe_input_stream_read; |
223 | input_stream_class->close_fn = gdk_pipe_input_stream_close; |
224 | } |
225 | |
226 | static void |
227 | gdk_pipe_input_stream_init (GdkPipeInputStream *pipe) |
228 | { |
229 | } |
230 | |
231 | /* OUTPUT STREAM */ |
232 | |
233 | #define GDK_TYPE_PIPE_OUTPUT_STREAM (gdk_pipe_output_stream_get_type ()) |
234 | #define GDK_PIPE_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_PIPE_OUTPUT_STREAM, GdkPipeOutputStream)) |
235 | #define GDK_IS_PIPE_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_PIPE_OUTPUT_STREAM)) |
236 | #define GDK_PIPE_OUTPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIPE_OUTPUT_STREAM, GdkPipeOutputStreamClass)) |
237 | #define GDK_IS_PIPE_OUTPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIPE_OUTPUT_STREAM)) |
238 | #define GDK_PIPE_OUTPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIPE_OUTPUT_STREAM, GdkPipeOutputStreamClass)) |
239 | |
240 | typedef struct _GdkPipeOutputStream GdkPipeOutputStream; |
241 | typedef struct _GdkPipeOutputStreamClass GdkPipeOutputStreamClass; |
242 | |
243 | struct _GdkPipeOutputStream |
244 | { |
245 | GOutputStream parent; |
246 | |
247 | GdkIOPipe *pipe; |
248 | }; |
249 | |
250 | struct _GdkPipeOutputStreamClass |
251 | { |
252 | GOutputStreamClass parent_class; |
253 | }; |
254 | |
255 | GType gdk_pipe_output_stream_get_type (void) G_GNUC_CONST; |
256 | |
257 | G_DEFINE_TYPE (GdkPipeOutputStream, gdk_pipe_output_stream, G_TYPE_OUTPUT_STREAM) |
258 | |
259 | static void |
260 | gdk_pipe_output_stream_finalize (GObject *object) |
261 | { |
262 | GdkPipeOutputStream *pipe = GDK_PIPE_OUTPUT_STREAM (object); |
263 | |
264 | g_clear_pointer (&pipe->pipe, gdk_io_pipe_unref); |
265 | |
266 | G_OBJECT_CLASS (gdk_pipe_output_stream_parent_class)->finalize (object); |
267 | } |
268 | |
269 | static gssize |
270 | gdk_pipe_output_stream_write (GOutputStream *stream, |
271 | const void *buffer, |
272 | gsize count, |
273 | GCancellable *cancellable, |
274 | GError **error) |
275 | { |
276 | GdkPipeOutputStream *pipe_stream = GDK_PIPE_OUTPUT_STREAM (stream); |
277 | GdkIOPipe *pipe = pipe_stream->pipe; |
278 | gsize amount; |
279 | |
280 | gdk_io_pipe_lock (pipe); |
281 | |
282 | switch (pipe->state) |
283 | { |
284 | case GDK_IO_PIPE_EMPTY: |
285 | pipe->buffer = (void *) buffer; |
286 | pipe->size = count; |
287 | pipe->state = GDK_IO_PIPE_OUTPUT_BUFFER; |
288 | while (pipe->size == count && |
289 | pipe->state == GDK_IO_PIPE_OUTPUT_BUFFER && |
290 | !pipe->input_closed) |
291 | g_cond_wait (cond: &pipe->cond, mutex: &pipe->mutex); |
292 | if (pipe->state == GDK_IO_PIPE_OUTPUT_BUFFER) |
293 | { |
294 | amount = count - pipe->size; |
295 | pipe->state = GDK_IO_PIPE_EMPTY; |
296 | pipe->size = 0; |
297 | if (pipe->input_closed && amount == 0) |
298 | amount = count; |
299 | } |
300 | else |
301 | { |
302 | amount = count; |
303 | } |
304 | break; |
305 | |
306 | case GDK_IO_PIPE_INPUT_BUFFER: |
307 | amount = MIN (count, pipe->size); |
308 | |
309 | memcpy (dest: pipe->buffer, src: buffer, n: amount); |
310 | pipe->size -= amount; |
311 | |
312 | if (pipe->size == 0) |
313 | pipe->state = GDK_IO_PIPE_EMPTY; |
314 | else |
315 | pipe->buffer += amount; |
316 | break; |
317 | |
318 | case GDK_IO_PIPE_OUTPUT_BUFFER: |
319 | default: |
320 | g_assert_not_reached (); |
321 | amount = 0; |
322 | break; |
323 | } |
324 | |
325 | g_cond_broadcast (cond: &pipe->cond); |
326 | gdk_io_pipe_unlock (pipe); |
327 | |
328 | return amount; |
329 | } |
330 | |
331 | static gboolean |
332 | gdk_pipe_output_stream_close (GOutputStream *stream, |
333 | GCancellable *cancellable, |
334 | GError **error) |
335 | { |
336 | GdkPipeOutputStream *pipe_stream = GDK_PIPE_OUTPUT_STREAM (stream); |
337 | GdkIOPipe *pipe = pipe_stream->pipe; |
338 | |
339 | gdk_io_pipe_lock (pipe); |
340 | |
341 | pipe->output_closed = TRUE; |
342 | |
343 | g_cond_broadcast (cond: &pipe->cond); |
344 | gdk_io_pipe_unlock (pipe); |
345 | |
346 | return TRUE; |
347 | } |
348 | |
349 | static void |
350 | gdk_pipe_output_stream_class_init (GdkPipeOutputStreamClass *class) |
351 | { |
352 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
353 | GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (class); |
354 | |
355 | object_class->finalize = gdk_pipe_output_stream_finalize; |
356 | |
357 | output_stream_class->write_fn = gdk_pipe_output_stream_write; |
358 | output_stream_class->close_fn = gdk_pipe_output_stream_close; |
359 | } |
360 | |
361 | static void |
362 | gdk_pipe_output_stream_init (GdkPipeOutputStream *pipe) |
363 | { |
364 | } |
365 | |
366 | /* IOSTREAM */ |
367 | |
368 | #define GDK_TYPE_PIPE_IO_STREAM (gdk_pipe_io_stream_get_type ()) |
369 | #define GDK_PIPE_IO_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_PIPE_IO_STREAM, GdkPipeIOStream)) |
370 | #define GDK_IS_PIPE_IO_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_PIPE_IO_STREAM)) |
371 | #define GDK_PIPE_IO_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIPE_IO_STREAM, GdkPipeIOStreamClass)) |
372 | #define GDK_IS_PIPE_IO_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIPE_IO_STREAM)) |
373 | #define GDK_PIPE_IO_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIPE_IO_STREAM, GdkPipeIOStreamClass)) |
374 | |
375 | typedef struct _GdkPipeIOStream GdkPipeIOStream; |
376 | typedef struct _GdkPipeIOStreamClass GdkPipeIOStreamClass; |
377 | |
378 | struct _GdkPipeIOStream |
379 | { |
380 | GIOStream parent; |
381 | |
382 | GInputStream *input_stream; |
383 | GOutputStream *output_stream; |
384 | GdkIOPipe *pipe; |
385 | }; |
386 | |
387 | struct _GdkPipeIOStreamClass |
388 | { |
389 | GIOStreamClass parent_class; |
390 | }; |
391 | |
392 | GType gdk_pipe_io_stream_get_type (void) G_GNUC_CONST; |
393 | |
394 | G_DEFINE_TYPE (GdkPipeIOStream, gdk_pipe_io_stream, G_TYPE_IO_STREAM) |
395 | |
396 | static void |
397 | gdk_pipe_io_stream_finalize (GObject *object) |
398 | { |
399 | GdkPipeIOStream *pipe = GDK_PIPE_IO_STREAM (object); |
400 | |
401 | g_clear_object (&pipe->input_stream); |
402 | g_clear_object (&pipe->output_stream); |
403 | g_clear_pointer (&pipe->pipe, gdk_io_pipe_unref); |
404 | |
405 | G_OBJECT_CLASS (gdk_pipe_io_stream_parent_class)->finalize (object); |
406 | } |
407 | |
408 | static GInputStream * |
409 | gdk_pipe_io_stream_get_input_stream (GIOStream *stream) |
410 | { |
411 | GdkPipeIOStream *pipe = GDK_PIPE_IO_STREAM (stream); |
412 | |
413 | return pipe->input_stream; |
414 | } |
415 | |
416 | static GOutputStream * |
417 | gdk_pipe_io_stream_get_output_stream (GIOStream *stream) |
418 | { |
419 | GdkPipeIOStream *pipe = GDK_PIPE_IO_STREAM (stream); |
420 | |
421 | return pipe->output_stream; |
422 | } |
423 | |
424 | static gboolean |
425 | gdk_pipe_io_stream_close (GIOStream *stream, |
426 | GCancellable *cancellable, |
427 | GError **error) |
428 | { |
429 | /* overwrite so we don't close the 2 streams */ |
430 | return TRUE; |
431 | } |
432 | |
433 | static void |
434 | gdk_pipe_io_stream_class_init (GdkPipeIOStreamClass *class) |
435 | { |
436 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
437 | GIOStreamClass *io_class = G_IO_STREAM_CLASS (class); |
438 | |
439 | object_class->finalize = gdk_pipe_io_stream_finalize; |
440 | |
441 | io_class->get_input_stream = gdk_pipe_io_stream_get_input_stream; |
442 | io_class->get_output_stream = gdk_pipe_io_stream_get_output_stream; |
443 | io_class->close_fn = gdk_pipe_io_stream_close; |
444 | } |
445 | |
446 | static void |
447 | gdk_pipe_io_stream_init (GdkPipeIOStream *pipe) |
448 | { |
449 | pipe->pipe = gdk_io_pipe_new (); |
450 | |
451 | pipe->input_stream = g_object_new (GDK_TYPE_PIPE_INPUT_STREAM, NULL); |
452 | GDK_PIPE_INPUT_STREAM (pipe->input_stream)->pipe = gdk_io_pipe_ref (pipe: pipe->pipe); |
453 | |
454 | pipe->output_stream = g_object_new (GDK_TYPE_PIPE_OUTPUT_STREAM, NULL); |
455 | GDK_PIPE_OUTPUT_STREAM (pipe->output_stream)->pipe = gdk_io_pipe_ref (pipe: pipe->pipe); |
456 | } |
457 | |
458 | /** |
459 | * gdk_pipe_io_stream_new: |
460 | * |
461 | * Creates a `GIOStream` whose input- and output-stream behave like a pipe. |
462 | * |
463 | * Data written into the output stream becomes available for reading on |
464 | * the input stream. |
465 | * |
466 | * Note that this is data transfer in the opposite direction to |
467 | * g_output_stream_splice(). |
468 | * |
469 | * Returns: a new `GIOStream` |
470 | */ |
471 | GIOStream * |
472 | gdk_pipe_io_stream_new (void) |
473 | { |
474 | return g_object_new (GDK_TYPE_PIPE_IO_STREAM, NULL); |
475 | } |
476 | |