2 * Copyright (C) 2009 Michael 'Mickey' Lauer <mlauer@vanille-media.de>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 // This is for testing how to best deal with the two distinct threads,
21 // it may evolve in a Application class in libeflvala that hides the
22 // nasty details of the two threads / mainloops involved.
24 // What we need next is a thread-safe Queue where the inter-thread
25 // communication happens. Plus, both the mainloops need a way to
26 // be notified of new commands in the Queue without polling.
27 // I'll resort to the same technique I used in the ELAN project.
29 public class Command
: GLib
.Object
33 public class QueueWithNotifier
<T
> : GLib
.Object
36 private static int counter
= 0;
37 private int readfd
= -1;
38 private int writefd
= -1;
40 public QueueWithNotifier()
43 var ok
= Posix
.pipe( fds
);
45 debug( "fds = %d, %d", fds
[0], fds
[1] );
53 Posix
.close( readfd
);
55 Posix
.close( writefd
);
61 var number
= Posix
.read( readfd
, dot
, 1 );
62 assert ( number
== 1 );
67 public int getReadFd()
72 public int getWriteFd()
78 public class BidirectionalThreadQueue
: GLib
.Object
80 public enum Identifier
85 private static QueueWithNotifier
<Command
> toGuiQ
;
86 private static QueueWithNotifier
<Command
> toCommQ
;
88 private Identifier owner
;
90 public BidirectionalThreadQueue( Identifier id
)
95 case Identifier
.COMMUNICATION_THREAD
:
96 assert ( toGuiQ
== null );
97 toGuiQ
= new QueueWithNotifier
<Command
>();
99 case Identifier
.GUI_THREAD
:
100 assert ( toCommQ
== null );
101 toCommQ
= new QueueWithNotifier
<Command
>();
104 assert_not_reached();
108 public void getFds( out int readfd
, out int writefd
)
112 case Identifier
.COMMUNICATION_THREAD
:
113 readfd
= toCommQ
.getReadFd();
114 writefd
= toGuiQ
.getWriteFd();
116 case Identifier
.GUI_THREAD
:
117 readfd
= toGuiQ
.getReadFd();
118 writefd
= toCommQ
.getWriteFd();
121 assert_not_reached();
125 public Command?
read()
129 case Identifier
.COMMUNICATION_THREAD
:
130 return toCommQ
.read();
132 case Identifier
.GUI_THREAD
:
133 return toGuiQ
.read();
136 assert_not_reached();
141 public class CommunicationThread
: GLib
.Object
145 public static CommunicationThread self
;
146 public static MainLoop loop
;
148 public BidirectionalThreadQueue q
;
149 private IOChannel ioc
;
151 public CommunicationThread()
153 assert ( self
== null );
155 q
= new
BidirectionalThreadQueue( BidirectionalThreadQueue
.Identifier
.COMMUNICATION_THREAD
);
158 public bool canReadFromQ( IOChannel source
, IOCondition c
)
160 debug( "G thread can read from Q" );
161 var command
= q
.read();
165 public bool watcher()
167 debug( "G mainloop still running" );
177 public void shutdown()
182 public static void* run()
184 assert ( self
!= null );
185 loop
= new
MainLoop( null, false );
186 Timeout
.add_seconds( 1, self
.watcher
);
189 self
.q
.getFds( out readfd
, out writefd
);
190 self
.ioc
= new GLib
.IOChannel
.unix_new( readfd
);
191 self
.ioc
.add_watch( IOCondition
.IN
, self
.canReadFromQ
);
192 debug( "INTO G mainloop" );
194 debug( "OUT OF G mainloop" );
199 public class UserInterfaceThread
: GLib
.Object
201 public static UserInterfaceThread self
;
202 public CommunicationThread commthread
;
203 public Ecore
.Timer timer
;
206 public BidirectionalThreadQueue q
;
210 public UserInterfaceThread( string[] args
)
212 assert ( self
== null );
215 q
= new
BidirectionalThreadQueue( BidirectionalThreadQueue
.Identifier
.GUI_THREAD
);
219 win
= new Elm
.Win( null, "myWindow", Elm
.WinType
.BASIC
);
220 win
.title_set( "Elementary meets Vala" );
221 win
.autodel_set( true );
222 win
.resize( 320, 320 );
223 win
.smart_callback_add( "delete-request", Elm
.exit
);
226 var layout
= new Elm
.Layout( win
);
227 layout
.file_set( "/usr/local/share/elementary/objects/test.edj", "layout" );
228 layout
.size_hint_weight_set( 1.0, 1.0 );
230 win
.resize_object_add( layout
);
234 public void setCommThread( CommunicationThread commthread
)
236 this
.commthread
= commthread
;
239 ~UserInterfaceThread()
244 public bool watcher()
246 debug( "E mainloop still running" );
247 Posix
.write( writefd
, ".", 1 );
251 public static void* run()
253 self
.timer
= new Ecore
.Timer( 1, self
.watcher
);
256 self
.q
.getFds( out readfd
, out self
.writefd
);
259 debug( "INTO E mainloop" );
261 debug( "OUT OF E mainloop" );
262 self
.commthread
.shutdown();
268 static int main( string[] args
)
270 if ( !Thread
.supported() ) {
271 error( "Cannot run without threads.\n" );
275 var commt
= new
CommunicationThread();
276 var uit
= new
UserInterfaceThread( args
);
277 uit
.setCommThread( commt
);
284 t1
= Thread
.create( commt
.run
, true );
285 t2
= Thread
.create( uit
.run
, true );
287 catch ( ThreadError ex
)
289 error( "%s", ex
.message
);
296 debug( "all threads exited OK." );