The Select Event Loop sample
demonstrates how to add HTTP server functionality to your C application
and use a POSIX select event loop to wait for I/O events. AppWeb offers
several methods to wait for events. See the
Programming Paradigms document for further information.
The sample is a
single-threaded main program that listens on port 8888 for HTTP requests.
See also
the equivalent
C++ selectEventLoop sample.
Files
selectEventLoop.conf
Makefile
selectEventLoop.c
Configuration File
selectEventLoop.conf
DocumentRoot "."
Listen 8888
ThreadLimit 0
AddHandler copyHandler
The selectEventLoop.conf file is an AppWeb configuration
file. It is configured to run single-threaded and assumes that
the sample is being run from the current directory.
You should modify the DocumentRoot and Listen directives
to suit your application's needs.
Makefile
The Makefile will build on Windows or Linux. A Windows VS 6.0
project file is also supplied.
Typical output from the Makefile build is listed below. This is
the output on a Windows system:
cl -o selectEventLoop.exe selectEventLoop.c -Zi -Od -D_NDEBUG -W3 -nologo -MDd -FD -DWIN -D_DLL \
-D_MT -D_WINDOWS -DWIN32 -D_WIN32_WINNT=0x500 -D_X86_=1 -D_CRT_SECURE_NO_DEPRECATE -D_USRDLL \
-I../../../include ../../../bin/libappwebStatic.lib ws2_32.lib advapi32.lib user32.lib
Source Code
selectEventLoop.c
/*
* @file selectEventLoop.c
* @brief Embed the AppWeb server in a simple single-threaded C
* application that uses a select event loop.
* Copyright (c) Mbedthis Software LLC, 2003-2007. All Rights Reserved.
*/
/******************************* Includes *****************************/
#define UNSAFE_FUNCTIONS_OK 1
#include "appweb/appweb.h"
#if BLD_FEATURE_C_API_MODULE
/************************** Forward Declarations **********************/
static void eventLoop();
/********************************* Code *******************************/
int main(int argc, char** argv)
{
MaHttp *http; /* For the http service inside our app */
MaServer *server; /* For a HTTP server */
/*
* Initialize the run-time and give our app a name
* "selectEventLoop"
*/
mprCreateMpr("selectEventLoop");
/*
* Do the following two statements only if you want debug trace
*/
mprAddLogFileListener();
mprSetLogSpec("stdout:4");
/*
* Start run-time services. Zero means single-threaded.
*/
mprStartMpr(0);
/*
* Create the HTTP and server objects. Give the server a name
* "default" and define "." as the default serverRoot, ie. the
* directory with the server configuration files.
*/
http = maCreateHttp();
server = maCreateServer(http, "default", ".");
/*
* Activate the copy handler. Only needed when linking statically.
*/
mprCopyInit(0);
/*
* Configure the server based on the directives in
* selectEventLoop.conf.
*/
if (maConfigureServer(server, "selectEventLoop.conf") < 0) {
fprintf(stderr,
"Can't configure the server. Error on line %d\n",
maGetConfigErrorLine(server));
exit(2);
}
/*
* Start serving pages. After this we are live.
*/
if (maStartServers(http) < 0) {
fprintf(stderr, "Can't start the server\n");
exit(2);
}
/*
* Service events. This call will block until the server is exited
* Call mprTerminate() at any time to instruct the server to exit.
*/
eventLoop();
/*
* Stop all HTTP services
*/
maStopServers(http);
/*
* Delete the server and http objects
*/
maDeleteServer(server);
maDeleteHttp(http);
/*
* Stop and delete the run-time services
*/
mprStopMpr();
mprDeleteMpr();
return 0;
}
/**********************************************************************/
/*
* Sample main event loop using select. This demonstrates how to
* integrate AppWeb with your applications event loop using select()
*/
static void eventLoop()
{
struct timeval timeout;
fd_set readFds, writeFds, exceptFds;
fd_set readInterest, writeInterest, exceptInterest;
#if WIN
fd_set *readp, *writep, *exceptp;
#endif
int maxFd, till, lastGet, readyFds;
lastGet = -1;
maxFd = 0;
FD_ZERO(&readInterest);
FD_ZERO(&writeInterest);
FD_ZERO(&exceptInterest);
while (!mprIsExiting()) {
if (mprRunTimers() > 0) {
till = 0;
} else {
till = mprGetIdleTime();
}
//
// This will run tasks if maxThreads == 0 (single threaded). If
// multithreaded, the thread pool will run tasks
//
if (mprRunTasks() > 0) { // Returns > 0 if more work to do
till = 0; // So don't block in select
}
//
// Mpr will populate with the FDs in use by MR on if necessary
//
if (mprGetFds(&readInterest, &writeInterest, &exceptInterest,
&maxFd, &lastGet)) {
//
// Masks have been rebuilt, so add user fds here ....
//
}
//
// Copy as select will modify readFds etc.
//
memcpy((void*) &readFds, (void*) &readInterest, sizeof(readFds));
memcpy((void*) &writeFds, (void*) &writeInterest, sizeof(readFds));
memcpy((void*) &exceptFds, (void*) &exceptInterest, sizeof(exceptFds));
timeout.tv_sec = till / 1000;
timeout.tv_usec = (till % 1000) * 1000;
#if WIN
//
// Windows does not accept empty descriptor arrays
//
readp = (readFds.fd_count == 0) ? 0 : &readFds;
writep = (writeFds.fd_count == 0) ? 0 : &writeFds;
exceptp = (exceptFds.fd_count == 0) ? 0 : &exceptFds;
readyFds = select(maxFd, readp, writep, exceptp, &timeout);
#else
readyFds = select(maxFd, &readFds, &writeFds, &exceptFds, &timeout);
#endif
if (readyFds > 0) {
mprServiceIO(readyFds, &readFds, &writeFds, &exceptFds);
}
}
}
/**********************************************************************/
#else /* BLD_FEATURE_C_API_MODULE */
int main()
{
fprintf(stderr, "BLD_FEATURE_C_API_MODULE is not defined in config.h\n");
exit(2);
}
#endif /* BLD_FEATURE_C_API_MODULE */