Figure 1 Counter.c
/*************************************************************
Module name: Counter.c
Notices: 1996 Jeffrey Richter
Description: Demonstrates background processing using fibers
*************************************************************/
#define STRICT
#define _WIN32_WINNT 0x0400 // for Fiber function prototypes
#include <Windows.h>
#include <WindowsX.h>
#include "Resource.h"
//////////////////////////////////////////////////////////////
// The possible state of the background processing
typedef enum {
BPS_STARTOVER, // Start the background processing from
// beginning
BPS_CONTINUE, // Continue the background processing
BPS_DONE // There is no background processing to do
} BKGNDPROCSTATE;
typedef struct {
// User-interface fiber execution context
PVOID pFiberUI;
// Handle of main UI window
HWND hwnd;
// State of background processing
BKGNDPROCSTATE bps;
} FIBERINFO, *PFIBERINFO;
// A global that contains application state information. This
// global is accessed directly by the UI fiber and indirectly
// by the background processing fiber.
FIBERINFO g_FiberInfo;
//////////////////////////////////////////////////////////////
void WINAPI Counter_FiberFunc (LPVOID pvParam) {
PFIBERINFO pFiberInfo = (PFIBERINFO) pvParam;
BOOL fTranslated;
int x, nCount;
// Update the window showing which fiber is executing
SetDlgItemText(pFiberInfo->hwnd,
IDC_FIBER, __TEXT("Recalculation"));
// Get the current count in the EDIT control
nCount = GetDlgItemInt(
pFiberInfo->hwnd, IDC_COUNT, &fTranslated, FALSE);
// Count from 0 to nCount updating the STATIC control
for (x = 0; x <= nCount; x++) {
// UI events have higher priority than counting.
// If there are any UI events, handle them ASAP.
if (HIWORD(GetQueueStatus(QS_ALLEVENTS)) != 0) {
// The UI fiber has something to do, temporarily
// pause counting, and handle the UI events.
SwitchToFiber(pFiberInfo->pFiberUI);
// The UI has no more events, continue counting
SetDlgItemText(pFiberInfo->hwnd,
IDC_FIBER, __TEXT("Recalculation"));
}
// Update the STATIC control with the most recent count
SetDlgItemInt(pFiberInfo->hwnd, IDC_ANSWER, x, FALSE);
// Sleep for a while to exaggerate the effect; remove
// the call to Sleep in production code.
Sleep(200);
}
// Indicate that counting is complete.
pFiberInfo->bps = BPS_DONE;
// Reschedule the UI thread. When the UI thread is running
// and has no events to process, the thread is put to sleep
// NOTE: If we just allow the fiber function to return,
// the thread and the UI fiber die -- we don't want this!
SwitchToFiber(pFiberInfo->pFiberUI);
}
//////////////////////////////////////////////////////////////
BOOL Counter_OnInitDialog(HWND hwnd,
HWND hwndFocus, LPARAM lParam) {
#ifdef _DEBUG
// In debug versions, move the window to the top, left so
// that the rest of the screen is available to the debugger.
SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
#endif
SetDlgItemInt(hwnd, IDC_COUNT, 0, FALSE);
return(TRUE);
}
//////////////////////////////////////////////////////////////
void Counter_OnCommand(HWND hwnd,int id, HWND hwndCtl,
UINT codeNotify) {
switch (id) {
case IDCANCEL:
// If the Escape key is hit, destroy the modeless
// dialog box, terminating the application.
DestroyWindow(hwnd);
break;
case IDC_COUNT:
if (codeNotify == EN_CHANGE) {
// When the user changes the count, start the
// background processing over from the beginning
g_FiberInfo.bps = BPS_STARTOVER;
}
break;
}
}
//////////////////////////////////////////////////////////////
BOOL WINAPI Counter_DlgProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
HANDLE_MSG(hwnd, WM_INITDIALOG, Counter_OnInitDialog);
HANDLE_MSG(hwnd, WM_COMMAND, Counter_OnCommand);
}
return(FALSE);
}
//////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hinstExe,
HINSTANCE hinstExePrev, LPSTR pszCmdLine, int nCmdShow) {
// Counter fiber execution context
PVOID pFiberCounter = NULL;
// Convert this thread to a fiber
g_FiberInfo.pFiberUI = ConvertThreadToFiber(NULL);
// Create the appication's UI window
g_FiberInfo.hwnd = CreateDialog(hinstExe,
MAKEINTRESOURCE(IDD_COUNTER), NULL, Counter_DlgProc);
// Update the window showing which fiber is executing
SetDlgItemText(g_FiberInfo.hwnd,
IDC_FIBER, __TEXT("User-interface"));
// Initially, there is no background processing to be done
g_FiberInfo.bps = BPS_DONE;
// While the UI window still exists...
while (IsWindow(g_FiberInfo.hwnd)) {
// UI messages are higher priority
// than background processing
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
// If a message exists in the queue, process it
if (!IsDialogMessage(g_FiberInfo.hwnd, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} else {
// If no UI messages exist, check the
// state of the background processing
switch (g_FiberInfo.bps) {
case BPS_DONE:
// No messages exist and there is no background
// processing to do; wait for a UI event
WaitMessage();
break;
case BPS_STARTOVER:
// The user has changed the count, start the
// background processing over from the beginning.
if (pFiberCounter != NULL) {
// If a recaulculation fiber already exists,
// delete it so that background processing
// will start all over from the beginning
DeleteFiber(pFiberCounter);
pFiberCounter = NULL;
}
// Create a new recalc fiber that
// starts over from the beginning
pFiberCounter = CreateFiber(
0, Counter_FiberFunc, &g_FiberInfo);
// Indicate that we have started the background
// processing and that it should continue
g_FiberInfo.bps = BPS_CONTINUE;
// Fall through to BPS_CONTINUE case...
case BPS_CONTINUE:
// Allow the background processing to execute...
SwitchToFiber(pFiberCounter);
// The background processing has been paused (because
// a UI message showed up) or has been stopped
// (because the counting completed).
// Update the window showing which fiber is executing
SetDlgItemText(g_FiberInfo.hwnd,
IDC_FIBER, __TEXT("User-interface"));
if (g_FiberInfo.bps == BPS_DONE) {
// If the background processing ran to completion,
// delete the background fiber so that background
// processing will start all over from the
// beginning next time
DeleteFiber(pFiberCounter);
pFiberCounter = NULL;
}
break;
} // switch on background processing state
} // No UI messages exist
} // while the window still exists
return(0); // End the application
}
//////////////////////// End Of File /////////////////////////
Figure 3 FiberInfo Variable State Values
BPS_DONE | The recalculation ran to completion and the user has not changed anything that would require a recalculation. |
BPS_STARTOVER | The user changed something that requires restarting the recalculation from the very beginning. |
BPS_CONTINUE | The recalculation was started but has not finished; the user has not changed anything that would require restarting the recalculation from the beginning. |