Release Notes QP v4 Development Kit for Win32 with 1-Thread Quantum Leaps, LLC, www.quantum-leaps.com May 07, 2008 These release notes provide information on the QP Development Kit (QDK) for Win32 using 1 Win32 thread. The main purpose of this QDK is to provide a desktop emulation of QP for developing and testing the embedded QP applications based on the non-preemptive "Vanilla" scheduler available in QF. This QDK assumes Microsoft Visual C++ v6.0. However, the QDK has been also tested with the newer Visual C++ 2005 and 2008 edtions. The newer versions of Visual C++ tranlsate automatically the provided workspace and project files. Licensing --------- This QDK is part of the Quantum Leaps QP event-driven platform, and may be distributed and modified under the terms of the GNU General Public License version 2 (GPL) as published by the Free Software Foundation and appearing in the file GPL.TXT included in the packaging of this file. Please note that GPL Section 2[b] requires that all works based on this software must also be made publicly available under the terms of the GPL ("Copyleft"). Alternatively, this software may be distributed and modified in conjunction with a valid Quantum Leaps commercial license. Quantum Leaps commercial licenses are designed for users who want to retain proprietary status of their code. The users who license this software under one of Quantum Leaps commercial licenses do not use this software under the GPL and therefore are not subject to any of its terms. Contact information: Quantum Leaps Web site: http://www.quantum-leaps.com Quantum Leaps downloads: http://www.quantum-leaps.com/downloads/ e-mail: info@quantum-leaps.com Installing the QDK ------------------ To avoid unnecessary and difficult to maintain repetitions, the QDKs contain only the code pertinent to the specific platform, but do not contain the platform-independent Quantum Leaps baseline code, which is available separately from http://www.quantum-leaps.com/downloads. Each QDK is provided as a ZIP archive that is designed to "plug-into" the directory structure already established after the installation of the QP baseline code. he new QP directory structure is described in a separate Applicaiotn Note "QP Directory Structure", available online from: http://www.quantum-leaps.com/doc/AN_QP_Directory_Structure.pdf. Before installing this QDK you should install the compatible version of the QP baseline code. To install this QDK, download the qdkc_win32_1t_xxx.ZIP file and uncompress it into the SAME "QP Root Directory", in which you installed QP. For the sake of further discussion, this direcory will be referenced as . The following diagram shows the essential directories and files that should be present in the installation directory after you've performed the correct installation of this QDK. The files and directories marked with a (*) are included in this QDK: / - QP-Root Directory | +-gpl.txt - (*) GNU General Public License | +-doxygen/ - HTML Doxygen documentation generated from the source | +-include/ - QP platform-independent include files | +-qassert.h - QEP platform-independent assertions | +-qevent.h - QEvent declaration | +-qep.h - QEP platform-independent public include file | +-qf.h - QF platform-independent public include file | +-qequeue.h - QF Event Queue public include file | +-qmpool.h - QF Memory Pool public include file | +-qpset.h - QF Priority Set public include file | +-qvanilla.h - QF Non-preemptive scheduler public include file | +-examples/ - QP examples | +-80x86/ - QP examples for 80x86 CPU architecture | | +-win32_1t/ - Win32-1Thread examples | | | +-vc6/ - Visual C 6.0 compiler | | | | +-dpp/ - (*) Dining Philosophers example application | | | | | +-Debug/ - (*) Object files and executable for Debug build | | | | | +-Release/ - (*) Object files and executable for Release build | | | | | +-dpp.dsp - (*) Visual Studio project to build the application | | | | | +-dpp.dsw - (*) Visual Studio workspace to build the application | | | | | | | | | +-dpp-gui/ - (*) Dining Philosophers example with GUI | | | | | +-Debug/ - (*) Object files and executable for Debug build | | | | | +-Release/ - (*) Object files and executable for Release build | | | | | +-dpp-gui.dsp - (*) Visual Studio project to build the application | | | | | +-dpp-gui.dsw - (*) Visual Studio workspace to build the application | | | | | | +-ports/ - QP ports | +-80x86/ - (*) QP ports to 80x86 CPU architecture | | +-win32_1t/ - (*) Win32-1Thread ports | | | +-vc6/ - (*) Visual C 6.0 compiler | | | | +-Debug/ - (*) QP libraries for Debug build | | | | | +-qep.lib - (*) QEP library for Debug build | | | | | +-qf.lib - (*) QF library for Debug build | | | | +-Release/ - (*) QP libraries for Release build | | | | | +-qep.lib - (*) QEP library for Release build | | | | | +-qf.lib - (*) QF library for Release build | | | | +-Spy/ - (*) QP libraries for Release build | | | | | +-qep.lib - (*) QEP library for Spy build | | | | | +-qf.lib - (*) QF library for Spy build | | | | | +-qs.lib - (*) QS library for Spy build | | | | +-qep_port.h - (*) QEP platform-dependent include file | | | | +-qf_port.h - (*) QF platform-dependent include file | | | | +-qs_port.h - (*) QS platform-dependent include file | | | | +-qp_port.h - (*) QP platform-dependent include file | NOTE1: As indicated in the directory/file description, Visual Studio workspaces and project files for building the QEP library, the QF library, the QS library, and the DPP executable are provided. About the QDK ------------- This QDK uses only 1 thread (the main thread) to execute all active objects in the application. The system clock tick is provided by a separate Win32 thread. This QDK is based on the "vanilla" QF port, as described in the book PSiCC2. In particular, the QDK uses the modified native non-preemptive scheduler built into QF. QF Critical Section and Scheduler Locking ----------------------------------------- QF, like all real-time frameworks, needs to execute certain sections of code indivisibly to avoid data corruption. The most straightforward way of protecting such critical sections of code is disabling and enabling interrupts, which Win32 does not allow. This QF port uses therefore a single package-scope Win32 critical section object QF_win32CritSect_ to protect all critical sections. Here are the QF macros for protecting and unprotecting a critical section from the file /ports/80x86/win32_1t/vc6/qf_port.h: /* Win32 critical section entry/exit */ /* QF_INT_KEY_TYPE not defined */ #define QF_INT_LOCK(key_) EnterCriticalSection(&QF_win32CritSect_) #define QF_INT_UNLOCK(key_) LeaveCriticalSection(&QF_win32CritSect_) Using the single critical section object for all crtical section guarantees that only one thread at a time can execute inside a critical section. This prevents race conditions and data corruption. Please note, however, that the Win32 critical section implementation behaves differently than interrupt locking. A common Win32 critical section ensures that only one thread at a time can execute a critical section, but it does not guarantee that a context switch cannot occur within the critical section. In fact, such context switches probably will happen, but they should not cause concurrency hazards because the critical section eliminates all race conditionis. Unlinke simply disabling and enabling interrupts, the critical section approach is also subject to priority inversions. Various versions of Windows handle priority inversions differently, but it seems that most of them recognize priority inversions and dynamically adjust the priorities of threads to prevent it. Please refer to the MSN articles for more information. QF Clock Tick ------------- As always with desktop systems, in Windows it's difficult to invoke QF_tick() from the timer interrupt context. Instead, QF_tick() is invoked from a separate Win32 thread. **** NOTE: In QP v4 the QF_tick() function can run safely in both the ISR and task level. In this port it runs in the task-level context. **** NOTE: You can adjust the clock tick rate by means of the function QF_setTickRate(). This function takes the ticksPerSec argument, which denotes the desired number of clock ticks per second (i.e., the ticking rate in Hz). **** static DWORD WINAPI tickerThread(LPVOID) { // signature for CreateThread() for (;;) { Sleep(l_tickMsec); // wait for the specified tick period QF_tick(); // perform the QF clock tick processing } return 0; // return success } The "ticker thread" is started from QF_run(), as follows: void QF_run(void) { /* see NOTE01 */ DWORD threadId; HANDLE hThread; QF_onStartup(); /* startup callback */ l_running = (uint8_t)1; /* create the ticker thread */ hThread = CreateThread(NULL, 1024, &tickerThread, (void *)0, 0, &threadId); Q_ASSERT(hThread != (HANDLE)0); /* thread must be created */ SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL); while (l_running) { /* the background loop */ QF_INT_LOCK_KEY_ QF_INT_LOCK_(); if (QPSet64_notEmpty(&QF_readySet_)) { uint8_t p; QActive *a; QEvent const *e; QPSet64_findMax(&QF_readySet_, p); QF_INT_UNLOCK_(); a = QF_active_[p]; e = QActive_get_(a);/*get the next event for this active object */ QF_ACTIVE_DISPATCH_(&a->super, e); /* dispatch to state machine */ QF_gc(e); /* determine if event is garbage and collect it if so */ } else { #ifndef QF_INT_KEY_TYPE QF_onIdle(); /* see NOTE02 */ #else QF_onIdle(intLockKey_); /* see NOTE02 */ #endif /* QF_INT_KEY_TYPE */ } } QF_onCleanup(); /* cleanup callback */ QS_EXIT(); CloseHandle(QF_win32Event_); DeleteCriticalSection(&QF_win32CritSect_); } Please note that the priority of the ticker thread is set to the level of "THREAD_PRIORITY_TIME_CRITICAL", to provide as timely ticking as possible. Please note, however, that due to the non-preemptive character of the QF scheduler running all active objects in the single thread (the main() thread), the actual processing of the timeout events can be delayed by the full (longest) Run-To-Completion step in the whole system. QF Idle Processing ------------------ When all events are processed, the non-preemptive scheduler QF_run() invokes the user-defined callback QF_onIdle(). This callback is invoked with interrupts LOCKED, because the clock tick thread can asynchronously post an event to an active object and thus change the idle condition. Therefore, at a minimum, the QF_onIdle() must unlock interrupts (by calling QF_INT_UNLOCK()). Also, to prevent the main application thread from taking all the CPU cycles, the QF_onIdle() callback should invoke the macro QF_WAIT_FOR_EVENTS(). This latter macro uses the Win32 call WaitForSingleObject() to efficiently block the main thread until a event arrives into an active object's event queue. Quantum Spy Instrumentation --------------------------- The qdpp-spy example application included in this QDK uses the Windows sockets (TCP/IP) to send out the tracing infromation from the application to the QSpy host utilty. The QSpy host utility provides the server TCP/IP socket and must necessarily be launched BEFORE starting the QP application. The QSpy utility should be started from a command line with the following parameters: qspy -t -S2 -E4 -Q4 -P4 -B4 -C4 Problems, Questions ------------------- If you find any problems with this port, or simply have questions, please drop an e-mail to dev@quantum-leaps.com. You might also consult the online discussion forum at http://pub50.bravenet.com/forum/4273862864. Quantum Leaps, LLC www.quantum-leaps.com