switch to new MPL based Fraunhofer FOKUS Public License
[senf.git] / senf / Scheduler / Scheduler.test.cc
1 // $Id$
2 //
3 // Copyright (C) 2006
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 //
6 // The contents of this file are subject to the Fraunhofer FOKUS Public License
7 // Version 1.0 (the "License"); you may not use this file except in compliance
8 // with the License. You may obtain a copy of the License at 
9 // http://senf.berlios.de/license.html
10 //
11 // The Fraunhofer FOKUS Public License Version 1.0 is based on, 
12 // but modifies the Mozilla Public License Version 1.1.
13 // See the full license text for the amendments.
14 //
15 // Software distributed under the License is distributed on an "AS IS" basis, 
16 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
17 // for the specific language governing rights and limitations under the License.
18 //
19 // The Original Code is Fraunhofer FOKUS code.
20 //
21 // The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V. 
22 // (registered association), Hansastraße 27 c, 80686 Munich, Germany.
23 // All Rights Reserved.
24 //
25 // Contributor(s):
26 //   Stefan Bund <g0dil@berlios.de>
27
28 /** \file
29     \brief Scheduler unit tests */
30
31 //#include "Scheduler.test.hh"
32 //#include "Scheduler.test.ih"
33
34 // Custom includes
35 #include <sys/types.h>
36 #include <signal.h>
37 #include <sys/wait.h>
38 #include <unistd.h>
39 #include <sys/socket.h>
40 #include <sys/un.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <iostream>
44 #include <boost/bind.hpp>
45
46 #include "Scheduler.hh"
47
48 #include <senf/Utils/auto_unit_test.hh>
49 #include <boost/test/test_tools.hpp>
50
51 #define prefix_
52 //-/////////////////////////////////////////////////////////////////////////////////////////////////
53
54 namespace {
55
56     char const * SOCK_PATH = "/tmp/sched_test.sock";
57
58     void error(char const * fn, char const * proc="")
59     {
60         std::cerr << "\n" << proc << fn << ": " << strerror(errno) << std::endl;
61     }
62
63     void fail(char const * fn)
64     {
65         error(fn,"server: ");
66         _exit(1);
67     }
68
69     void server()
70     {
71         int sock = socket(PF_UNIX,SOCK_STREAM,0);
72         if (sock<0) fail("socket");
73         struct sockaddr_un sun;
74         memset(&sun,0,sizeof(sun));
75         sun.sun_family = AF_UNIX;
76         strcpy(sun.sun_path,SOCK_PATH);
77         if (bind(sock,(struct sockaddr*)&sun,sizeof(sun))<0) fail("bind");
78         if (listen(sock,1)<0) fail("listen");
79         int conn = accept(sock,0,0);
80         if (conn < 0) fail("accept");
81
82         //-/////////////////////////////////////////////////////////////////////////////////////////
83
84         if (write(conn,"READ",4)<0) fail("write");
85         char buffer[1024];
86         int size =  read(conn,buffer,1024);
87         if (size<0) fail("read");
88         if (size == 5) {
89             buffer[5] = 0;
90             if (strcmp(buffer,"WRITE")==0) {
91                 if (write(conn,"OK",2)<0) fail("write");
92             } else
93                 if (write(conn,"FAIL",4)<0) fail("write");
94         } else
95             if (write(conn,"FAIL",4)<0) fail("write");
96
97         //-/////////////////////////////////////////////////////////////////////////////////////////
98
99         close(conn);
100         close(sock);
101     }
102
103     int start_server()
104     {
105         unlink(SOCK_PATH);
106         int pid = fork();
107         if (pid == 0) {
108             signal(SIGCHLD, SIG_IGN);
109             server();
110             _exit(0);
111         }
112         if (pid < 0) {
113             error("fork");
114             return 0;
115         }
116         signal(SIGCHLD, SIG_DFL);
117
118         sleep(1); // Wait for the server socket to be opened
119         return pid;
120     }
121
122     bool stop_server(int pid)
123     {
124         sleep(1); // Wait for the server to terminate
125         if (kill(pid,SIGTERM)<0) {
126             error("kill");
127             return false;
128         }
129         int status = 0;
130         if (waitpid(pid,&status,0)<0) {
131             error("waitpid");
132             return false;
133         }
134         unlink(SOCK_PATH);
135         if (WIFSIGNALED(status)) {
136             std::cerr << "\nserver terminated with signal " << WTERMSIG(status) << std::endl;
137             return false;
138         }
139         if (WEXITSTATUS(status)!=0) {
140             std::cerr << "\nserver terminated with exit status " << WEXITSTATUS(status) << std::endl;
141             return false;
142         }
143         return true;
144     }
145
146     char buffer[1024];
147     int size;
148     int event;
149
150     void callback(int fd, int ev)
151     {
152         event = ev;
153         switch (event & senf::scheduler::FdEvent::EV_ALL) {
154         case senf::scheduler::FdEvent::EV_READ:
155             size = recv(fd,buffer,1024,0);
156             break;
157         case senf::scheduler::FdEvent::EV_PRIO:
158             size = recv(fd,buffer,1024,MSG_OOB);
159             break;
160         case senf::scheduler::FdEvent::EV_WRITE:
161             size = write(fd,buffer,size);
162             break;
163         }
164         senf::scheduler::terminate();
165     }
166
167     bool timeoutCalled = false;
168     void timeout()
169     {
170         timeoutCalled = true;
171         senf::scheduler::terminate();
172     }
173
174     struct HandleWrapper
175     {
176         HandleWrapper(int fd,std::string const & tag) : fd_(fd), tag_(tag) {}
177         int fd_;
178         std::string tag_;
179     };
180
181     int retrieve_filehandle(HandleWrapper const & handle)
182     {
183         return handle.fd_;
184     }
185
186     void handleCallback(HandleWrapper const & handle, int event)
187     {
188         if (handle.tag_ != "TheTag")
189             return;
190         callback(handle.fd_,event);
191     }
192
193     bool is_close(senf::ClockService::clock_type a, senf::ClockService::clock_type b)
194     {
195         return (a<b ? b-a : a-b) < senf::ClockService::milliseconds(100);
196     }
197
198     senf::ClockService::clock_type sigtime (0);
199
200     void sigusr(siginfo_t const &)
201     {
202         sigtime = senf::ClockService::now();
203         senf::scheduler::terminate();
204     }
205
206     void delay(unsigned long milliseconds)
207     {
208         struct timespec ts;
209         ts.tv_sec = milliseconds / 1000;
210         ts.tv_nsec = (milliseconds % 1000) * 1000000;
211         while (nanosleep(&ts,&ts) < 0 && errno == EINTR) ;
212     }
213
214     void blockingHandler()
215     {
216         delay(2200);
217         senf::scheduler::terminate();
218     }
219
220     unsigned eventCount (0);
221
222     void eventeventhandler()
223     {
224         ++ eventCount;
225     }
226 }
227
228 void schedulerTest()
229 {
230     char const * enabled (getenv("SENF_TIMING_CRITICAL_TESTS"));
231     BOOST_WARN_MESSAGE(enabled, "Set SENF_TIMING_CRITICAL_TESTS to not skip timing critical tests");
232
233     int pid = start_server();
234     BOOST_REQUIRE (pid);
235
236     int sock = socket(PF_UNIX,SOCK_STREAM,0);
237     if (sock<0) {
238         error("socket");
239         BOOST_FAIL("socket");
240     }
241     struct sockaddr_un sun;
242     memset(&sun,0,sizeof(sun));
243     sun.sun_family = AF_UNIX;
244     strcpy(sun.sun_path,SOCK_PATH);
245
246     if (connect(sock,(struct sockaddr*)&sun,sizeof(sun))<0) {
247         error("connect");
248         BOOST_FAIL("connect");
249     }
250
251     //-/////////////////////////////////////////////////////////////////////////////////////////////
252
253     senf::scheduler::EventHook evev ("eventCounter", eventeventhandler,
254                                      senf::scheduler::EventHook::PRE);
255
256     {
257         senf::scheduler::FdEvent fde1 ("testFdEvent", boost::bind(&callback, sock, _1),
258                                       sock, senf::scheduler::FdEvent::EV_READ);
259         event = senf::scheduler::FdEvent::EV_NONE;
260         SENF_CHECK_NO_THROW( senf::scheduler::process() );
261         BOOST_CHECK_EQUAL( event, senf::scheduler::FdEvent::EV_READ );
262         BOOST_REQUIRE_EQUAL( size, 4 );
263         buffer[size]=0;
264         BOOST_CHECK_EQUAL( buffer, "READ" );
265
266         HandleWrapper handle(sock,"TheTag");
267         senf::scheduler::FdEvent fde2 ("testFdEvent", boost::bind(&handleCallback,handle,_1),
268                                       handle, senf::scheduler::FdEvent::EV_WRITE);
269         strcpy(buffer,"WRITE");
270         size=5;
271         event = senf::scheduler::FdEvent::EV_NONE;
272         SENF_CHECK_NO_THROW( senf::scheduler::process() );
273         BOOST_CHECK_EQUAL( event, senf::scheduler::FdEvent::EV_WRITE );
274
275         SENF_CHECK_NO_THROW( fde2.disable() );
276         event = senf::scheduler::FdEvent::EV_NONE;
277         sleep(1);
278         SENF_CHECK_NO_THROW( senf::scheduler::process() );
279         BOOST_CHECK_EQUAL( event, senf::scheduler::FdEvent::EV_READ|senf::scheduler::FdEvent::EV_HUP );
280         BOOST_REQUIRE_EQUAL( size, 2 );
281         buffer[size]=0;
282         BOOST_CHECK_EQUAL( buffer, "OK" );
283     }
284
285     {
286         senf::scheduler::TimerEvent timer1 ("testTimer1", &timeout,
287                                             senf::ClockService::now()+senf::ClockService::milliseconds(200));
288         senf::scheduler::TimerEvent timer2 ("testTimer2", &timeout,
289                                             senf::ClockService::now()+senf::ClockService::milliseconds(400));
290
291         event = senf::scheduler::FdEvent::EV_NONE;
292         senf::ClockService::clock_type t (senf::ClockService::now());
293         SENF_CHECK_NO_THROW( senf::scheduler::process() );
294         if (enabled)
295             BOOST_CHECK_PREDICATE( is_close, (senf::ClockService::now()-t) (senf::ClockService::milliseconds(200)) );
296         BOOST_CHECK( timeoutCalled );
297         BOOST_CHECK( ! timer1.enabled() );
298         BOOST_CHECK_EQUAL( event, senf::scheduler::FdEvent::EV_NONE );
299         if (enabled)
300             BOOST_CHECK_PREDICATE( is_close, (senf::ClockService::now()) (senf::scheduler::eventTime()) );
301         timeoutCalled = false;
302         SENF_CHECK_NO_THROW( senf::scheduler::process() );
303         if (enabled)
304             BOOST_CHECK_PREDICATE( is_close, (senf::ClockService::now()-t) (senf::ClockService::milliseconds(400)) );
305         BOOST_CHECK( timeoutCalled );
306         BOOST_CHECK_EQUAL( event, senf::scheduler::FdEvent::EV_NONE );
307         BOOST_CHECK( ! timer2.enabled() );
308
309         BOOST_MESSAGE( "A 'Scheduler task hanging' error is expected to be signaled here." );
310         SENF_CHECK_NO_THROW( timer1.action(&blockingHandler) );
311         SENF_CHECK_NO_THROW( timer1.timeout(senf::ClockService::now()) );
312         SENF_CHECK_NO_THROW( senf::scheduler::process() );
313         BOOST_CHECK_EQUAL( senf::scheduler::watchdogEvents(), 1u );
314     }
315
316     {
317         senf::scheduler::TimerEvent timer ("testWatchdog", &timeout,
318                                            senf::ClockService::now()+senf::ClockService::milliseconds(400));
319         senf::scheduler::SignalEvent sig (SIGUSR1, &sigusr);
320
321         senf::ClockService::clock_type t = senf::ClockService::now();
322         ::kill(::getpid(), SIGUSR1);
323         delay(200);
324         SENF_CHECK_NO_THROW( senf::scheduler::process() );
325         if (enabled) {
326             BOOST_CHECK_PREDICATE( is_close, (senf::ClockService::now()) (t+senf::ClockService::milliseconds(200)) );
327             BOOST_CHECK_PREDICATE( is_close, (sigtime) (t+senf::ClockService::milliseconds(200)) );
328         }
329         SENF_CHECK_NO_THROW( senf::scheduler::process() );
330     }
331
332     BOOST_CHECK( eventCount >= 8u );
333
334
335     //-/////////////////////////////////////////////////////////////////////////////////////////////
336
337     close(sock);
338
339     BOOST_CHECK (stop_server(pid));
340 }
341
342 SENF_AUTO_UNIT_TEST(testSchedulerPollTimers)
343 {
344     BOOST_CHECK( ! senf::scheduler::usingHiresTimers() );
345     schedulerTest();
346 }
347
348 SENF_AUTO_UNIT_TEST(testSchedulerHiresTimers)
349 {
350     if (senf::scheduler::haveScalableHiresTimers())
351         BOOST_MESSAGE( "Using timerfd() hires timers" );
352     else
353         BOOST_MESSAGE( "Using POSIX hires timers");
354     SENF_CHECK_NO_THROW( senf::scheduler::hiresTimers() );
355     BOOST_CHECK( senf::scheduler::usingHiresTimers() );
356     schedulerTest();
357     SENF_CHECK_NO_THROW( senf::scheduler::loresTimers() );
358     BOOST_CHECK( ! senf::scheduler::usingHiresTimers() );
359 }
360
361 SENF_AUTO_UNIT_TEST(testSchedulerPOSIXTimers)
362 {
363     if (senf::scheduler::haveScalableHiresTimers()) {
364         SENF_CHECK_NO_THROW( senf::scheduler::detail::TimerDispatcher::instance().timerSource(
365                                  std::auto_ptr<senf::scheduler::detail::TimerSource>(
366                                      new senf::scheduler::detail::POSIXTimerSource())) );
367         schedulerTest();
368         senf::scheduler::loresTimers();
369     }
370 }
371
372 namespace {
373
374     void sigme()
375     {
376         senf::scheduler::BlockSignals signalBlocker;
377         signalBlocker.block();
378         signalBlocker.unblock();
379         BOOST_CHECK( ! signalBlocker.blocked() );
380         signalBlocker.unblock();
381         signalBlocker.block();
382         BOOST_CHECK( signalBlocker.blocked() );
383         ::kill(::getpid(), SIGUSR1);
384         delay(200);
385     }
386
387 }
388
389 SENF_AUTO_UNIT_TEST(blockSignals)
390 {
391     char const * enabled (getenv("SENF_TIMING_CRITICAL_TESTS"));
392     BOOST_WARN_MESSAGE(enabled, "Set SENF_TIMING_CRITICAL_TESTS to not skip timing critical tests");
393
394     senf::scheduler::TimerEvent signaler ("sigme", &sigme, senf::ClockService::now());
395     senf::scheduler::TimerEvent timer (
396         "testWatchdog", &timeout, senf::ClockService::now()+senf::ClockService::milliseconds(400));
397     senf::scheduler::SignalEvent sig (SIGUSR1, &sigusr);
398
399     senf::ClockService::clock_type t = senf::ClockService::now();
400     SENF_CHECK_NO_THROW( senf::scheduler::process() );
401
402     if (enabled) {
403         BOOST_CHECK_PREDICATE( is_close,
404                                (senf::ClockService::now())
405                                (t+senf::ClockService::milliseconds(200)) );
406         BOOST_CHECK_PREDICATE( is_close, (sigtime) (t+senf::ClockService::milliseconds(200)) );
407     }
408
409     SENF_CHECK_NO_THROW( senf::scheduler::process() );
410 }
411
412 //-/////////////////////////////////////////////////////////////////////////////////////////////////
413 #undef prefix_
414
415 \f
416 // Local Variables:
417 // mode: c++
418 // fill-column: 100
419 // c-file-style: "senf"
420 // indent-tabs-mode: nil
421 // ispell-local-dictionary: "american"
422 // compile-command: "scons -u test"
423 // comment-column: 40
424 // End: