mainloops: more work on inter-thread-communication infrastructure
[libeflvala.git] / examples / library / mainloops.vala
blob0fbb44e8ce09afdb24d9c8fccb7584973d71f4ba
1 /**
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
35 private Queue<T> q;
36 private static int counter = 0;
37 private int readfd = -1;
38 private int writefd = -1;
40 public QueueWithNotifier()
42 int[] fds = { 0, 0 };
43 var ok = Posix.pipe( fds );
44 assert( ok != -1 );
45 debug( "fds = %d, %d", fds[0], fds[1] );
46 readfd = fds[0];
47 writefd = fds[1];
50 ~QueueWithNotifier()
52 if ( readfd != -1 )
53 Posix.close( readfd );
54 if ( writefd != -1 )
55 Posix.close( writefd );
58 public T read()
60 char[] dot = { '.' };
61 var number = Posix.read( readfd, dot, 1 );
62 assert ( number == 1 );
63 // generate T
64 return new Command();
67 public int getReadFd()
69 return readfd;
72 public int getWriteFd()
74 return writefd;
78 public class BidirectionalThreadQueue : GLib.Object
80 public enum Identifier
82 COMMUNICATION_THREAD,
83 GUI_THREAD;
85 private static QueueWithNotifier<Command> toGuiQ;
86 private static QueueWithNotifier<Command> toCommQ;
88 private Identifier owner;
90 public BidirectionalThreadQueue( Identifier id )
92 owner = id;
93 switch ( id )
95 case Identifier.COMMUNICATION_THREAD:
96 assert ( toGuiQ == null );
97 toGuiQ = new QueueWithNotifier<Command>();
98 break;
99 case Identifier.GUI_THREAD:
100 assert ( toCommQ == null );
101 toCommQ = new QueueWithNotifier<Command>();
102 break;
103 default:
104 assert_not_reached();
108 public void getFds( out int readfd, out int writefd )
110 switch ( owner )
112 case Identifier.COMMUNICATION_THREAD:
113 readfd = toCommQ.getReadFd();
114 writefd = toGuiQ.getWriteFd();
115 break;
116 case Identifier.GUI_THREAD:
117 readfd = toGuiQ.getReadFd();
118 writefd = toCommQ.getWriteFd();
119 break;
120 default:
121 assert_not_reached();
125 public Command? read()
127 switch ( owner )
129 case Identifier.COMMUNICATION_THREAD:
130 return toCommQ.read();
131 break;
132 case Identifier.GUI_THREAD:
133 return toGuiQ.read();
134 break;
135 default:
136 assert_not_reached();
141 public class CommunicationThread : GLib.Object
143 bool quitflag;
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 );
154 self = this;
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();
162 return true;
165 public bool watcher()
167 debug( "G mainloop still running" );
168 if ( !quitflag )
169 return true;
170 else
172 loop.quit();
173 return false;
177 public void shutdown()
179 quitflag = true;
182 public static void* run()
184 assert ( self != null );
185 loop = new MainLoop( null, false );
186 Timeout.add_seconds( 1, self.watcher );
187 int readfd;
188 int writefd;
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" );
193 loop.run();
194 debug( "OUT OF G mainloop" );
195 return null;
199 public class UserInterfaceThread : GLib.Object
201 public static UserInterfaceThread self;
202 public CommunicationThread commthread;
203 public Ecore.Timer timer;
204 public Elm.Win win;
206 public BidirectionalThreadQueue q;
208 int writefd = -1;
210 public UserInterfaceThread( string[] args )
212 assert ( self == null );
213 self = this;
215 q = new BidirectionalThreadQueue( BidirectionalThreadQueue.Identifier.GUI_THREAD );
217 Elm.init( args );
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 );
224 win.show();
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 );
229 layout.show();
230 win.resize_object_add( layout );
234 public void setCommThread( CommunicationThread commthread )
236 this.commthread = commthread;
239 ~UserInterfaceThread()
241 Elm.shutdown();
244 public bool watcher()
246 debug( "E mainloop still running" );
247 Posix.write( writefd, ".", 1 );
248 return true;
251 public static void* run()
253 self.timer = new Ecore.Timer( 1, self.watcher );
255 int readfd;
256 self.q.getFds( out readfd, out self.writefd );
257 //add fd handler...
259 debug( "INTO E mainloop" );
260 Elm.run();
261 debug( "OUT OF E mainloop" );
262 self.commthread.shutdown();
263 return null;
268 static int main( string[] args )
270 if ( !Thread.supported() ) {
271 error( "Cannot run without threads.\n" );
272 return 0;
275 var commt = new CommunicationThread();
276 var uit = new UserInterfaceThread( args );
277 uit.setCommThread( commt );
279 unowned Thread t1;
280 unowned Thread t2;
284 t1 = Thread.create( commt.run, true );
285 t2 = Thread.create( uit.run, true );
287 catch ( ThreadError ex )
289 error( "%s", ex.message );
290 return -1;
293 t1.join();
294 t2.join();
296 debug( "all threads exited OK." );
298 return 0;