Ai Factory Scheduling Multiple Problem Formulations

Document Sample
Ai Factory Scheduling Multiple Problem Formulations Powered By Docstoc
					                 Windowing Systems

• Windowing systems are probably the most non-standard
  aspect of modern computers, from a developer‟s point of

• There are many different windowing systems: Mac,
  Windows, X11, OS/2, NeXT, Java, Amiga, GEM,
  Desqview ...

• All have similar look and feel, and almost every element is
  common: windows, scrollbars, buttons, etc.

• The basic “event-driven” model is also the same for each.
• However, the programming interfaces for each system are
  all different, and this is where the difficulty lies for the

• There is a very large market these days for cross-platform
  GUI toolkits that allow cross-compilation on different
  windowing systems: qt, wxWindows, etc.

• This situation has allowed the most popular application
  platform (you know who) to acquire a large amount of
  platform specific software, because the costs required to
  produce separate versions of each program aren‟t always
  justified by the (often low) demand from the smaller
  markets such as Linux or the Macintosh.
     A brief history of windowing systems

• The first GUI with a WIMP interface (Windows, Icons,
  Mouse and Pull-down menu‟s) was created at Xerox
  PARC in the mid 1970‟s: the “Alto”.

• However, the elements that were used in the “Alto” and
  (later) in the “Star” were designed in the 1960‟s by Doug
  Engelbart, Alan Kay and others.

• The Macintosh was the first computer with a GUI to reach
  the mass market. Other windowing systems drew heavily
  upon the Mac‟s “look and feel.”
• We‟re not going to discuss each windowing system; that
  would take many lectures. We‟ll focus on the common
  aspects that are important to programmers.

• Ordinary text-based programs tend to have a structure that
  looks like this:

       get input;
       while (!end-of-input) {
       end program
• Sometimes the program might only take one set of inputs.
• Programs written for windowing systems are “event-
  driven.” This means that they have a main processing
  loop that looks like this:

       while (!done) {
             get next event;
             process event;
       end program;

• It may look similar to traditional programs, but there is a
  basic difference: “events” are more abstract than “input”
  and may come from many sources: the user, another
  application, the OS, even from across a network.
• In traditional programming, input is generally entered in a
  serial fashion and processed in order.

• Windowing events are deposited in an event queue in the
  order of their appearance and processed in that order.

• But windowed programs can process certain events, spawn
  threads to handle other events asynchronously or even
  completely ignore them!

• The different events handled by a windowing system
  include: mouse movements, mouse clicks, keyboard
  activity, redraw requests, button toggles, scroll requests,
  gain and loss of focus, etc.
• Window programs would be almost impossible to write if
  they had to implement each and every detail involved in
  detecting these events!

• Fortunately, the OS usually handles the hard parts,
  especially those that have to do with hardware.

• The OS (in the case of the Mac or Windows) or the
  windowing libraries (in the case of the various Unixen)
  “intercept” all user interaction. For each user action, they
  determine which window is affected (if any) and dispatch
  an event to the window‟s event queue.

• (Some OS‟s use one queue for all windows.)
• So a basic GUI program only has to do something like this:
  draw window to some specification;
  while (!done) {
      get an event; // An OS or GUI lib call
      switch (event_type) {
      case REDRAW:
            redraw window;
      case MOUSE_CLICK:
            determine location of click;
            move battleships to location;
            // ignore;
• If you write GUI code in a non-object oriented language
  (such as C) for any windowing system, your code will look
  just like that. E.g., Windows 3.1x code.

• There are a lot of details associated with this style of
  programming. There is usually a lot of code that is the
  duplicated from program to program. Programmers will
  find themselves writing more skeleton code than
  application code.

• In fact, the basic switch-based structure of this model is
  reminiscent of ... Implementations of object hierarchies
  (such as shapes) in non-object oriented languages.
void DrawShape (Type t, int x, int y, int size,
  Color c) {

    switch (t) {
    case TRIANGLE:
        DrawTriangle (x, y, size, c);
    case CIRCLE:
        DrawCircle (x, y, size / 2, c);
    case SQUARE:
        DrawSquare (x, y, size, c);
• We found a better way to express the notion of different
  shapes with common interfaces: an object hierarchy:

  class Shape {
      virtual void Draw (int x, int y, int size,
            Color c)= 0;

  class Circle : public Shape {
      void Draw (int x, int y, int size, Color c){
            // Draw a circle

  Class Square : public Shape { ... }
• Similarly, window applications lend themselves well to
  formulations as object oriented systems.

• But in order to use C++ constructs, the OS or windowing
  libraries have to be written in C++, right?

• Well, it helps. But there are many “frameworks” that are
  built on top of windowing systems that are written in C++.

• It‟s a simple concept: define a class “WindowObject” that
  implements every kind of event with virtual functions.
  Then, any class that inherits from WindowObject will
  automatically be GUI-aware; all it‟ll have to do is
  implement the events that it‟s interested it.
• This is how libraries like MFC operate: they sit on top of
  their OS‟s C-based windowing API (Application
  Programming Interface) and, with the help of a few
  wizards and some compiler-IDE support, actually make
  window program development rather easy.

• So with libraries like MFC presenting large collections of
  pre-programmed standard components and a collection of
  virtual event handlers to override, developers can spend
  more time developing the features of their programs and
  not re-implementing the same old window-control
• Window toolkits usually follow similar display standards:

  - Title bar: icon, title, management buttons
  - Menus: file, options, help...
  - Toolbars: save / load buttons, widgets...
  - Client area: graphics, bitmaps, text...
  - Status bar: messages, general information...

• Some systems can get quite complicated: for example,
  MFC parent-child window interaction is highly complex.

• In general, dialog box elements (checkboxes, radio
  buttons) are separate classes that can be inherited from and
  given custom behavior.
• We‟re not going to cover specifics in CS 213. A good site
  for MFC questions is

• Some general Win32 links can be found at and Microsoft‟s
  MSDN site.

• For other OS‟s, you‟re pretty much on your own. My
  advice would be to purchase a book that discusses your
  GUI toolkit in detail. There are good books at the campus
  store that discuss programming the MacOS, Motif, etc.
                          File I/O
• Files, as we‟ve seen, can be thought of as bidirectional data
  streams. This is the preferred C++ view, exemplified by
  standard library classes such as ifstream and ofstream.

• This is a good conceptual view of files, and it‟s especially
  useful for text-files, which store simple ASCII strings.

• For example, here‟s a program that writes a series of words
  from cin to a file and another program that reads the words
  from the file back to the screen.
#include <fstream.h>
void main() {
  char pszBuffer [1024];
  ofstream ofFile ("C:/Log.txt");
      if (!ofFile.is_open()) {
      cout << "Error opening file!" << endl;
  while (true) {
      cin >> pszBuffer;
      if (*pszBuffer == '/') {
      ofFile << pszBuffer << endl;
#include <fstream.h>
void main() {
  char pszBuffer [1024];
  ifstream ifFile ("C:/Log.txt");
  if (!ifFile.is_open()) {
      cout << "Error opening file!" << endl;
  ifFile >> pszBuffer;
  while (!ifFile.eof()) {
      cout << pszBuffer << " ";
      ifFile >> pszBuffer;
  cout << endl;
• However, sometimes programmers want to use lower-level
  operations in order to store data structures and objects in a

• For example, here‟s a function that writes the following
  struct to disk and another that restores it:

  struct Student {
      int StudentID;
      char StudentName [100];
      long TuitionRate;
      double GPA;
void SaveStudent (char* pszF, const Student& s) {
  ofstream ofFile (pszF, ios::binary);
  ofFile.write ((char*) &s, sizeof (Student));

void LoadStudent (char* pszF, Student& s){
  ifstream ifFile (pszF, ios::binary); ((char*) &s, sizeof (Student));

• These functions would be called like this:
void main() {

    Student s;
    s.StudentID = 45;
    strcpy (s.StudentName, "Max Feingold");
    s.TuitionRate = 10000;
    s.GPA = 4.3;

    SaveStudent ("C:/student.dat", s);

    Student s2;
    LoadStudent ("C:/student.dat", s2);
• General object serialization is more complicated. Because
  of the way C++ objects are laid out in memory (remember
  vtables?), a simple struct copy won‟t work.

• Also, an object may contain dynamically allocated
  memory or other objects that aren‟t easily serializable.

• In general, the best way to approach this problem is on an
  object-by-object basis, creating a framework in which
  every object implements the following static functions:

  void Save (File* file, Object& a);
  Object* Load (File* file);
• This scheme forces each class to implement on one hand a
  specific routine that saves an object to a file, and on the
  other, an “object factory” that knows how to create an
  object from file data.

• The Load function in particular can be made static and thus
  be called from anywhere in the client program‟s code.

• If a class contains objects as data: in this case, the specific
  Save() functions defined for these objects‟ classes can be
  nested into the main class‟s Save() function.

• Alternative approaches use constructors with File*
  arguments instead of Load(). The effect is the same.
• In general, a theoretical file class would present the
  following interface:

class File {
   int Open (char* pszFileName, int mode);
   int Close();
   bool IsOpen();
   int Read (char* pszBuffer, int iNumBytes);
   int Write (char* pszBuffer, int iNumBytes);
   int Seek (int iNumBytes;
   int GetPosition();
   int Search (char* pszBuffer, int iNumBytes);
   bool Eof();
• Note that this isn‟t meant to be a description of an actual
  file class that exists (although some existing classes, like
  MFC‟s CFile, are somewhat similar).

• Rather, it is meant to be a template for understanding what
  existing file I/O classes or file libraries do.

• The simplest file I/O library to use is probably the standard
  C library, which has four simple primitives:
   –   fopen();
   –   fread();
   –   fwrite();
   –   fclose();
• When working on your projects, feel free to use the
  libraries that suit your program the best. We encourage
  you to use the stream redirection operators and the C++
  standard library whenever possible, but there are cases
  where simpler solutions are better.

• Remember that the C++ standard library classes and the C
  library present interfaces that are portable across multiple
  platforms. Other possibilities, such as MFC classes, will
  only work with Windows.

• Sockets were originally designed in a C-based UNIX

• In UNIX, just about every system “object” (such as files,
  pipes or devices) presents a simple open/read/write/close

• It seemed logical to the sockets designers to extend this
  paradigm to networks as well. The idea is a natural one:
  open a socket, read from it, write data to it, and finally
  close it.
• So, what exactly is a socket? Well, sockets can be thought
  of as logical pipes that connect two computers over some
  physical connection medium (such as the telephone line or

• There must exist a large infrastructure beneath the sockets
  level for connections to be established:
   – Physical connection medium.
   – Routers.
   – OS support for devices: modems, ethernet.
   – OS support for low-level protocols: IP
   – OS support for higher-level protocols: TCP, UDP, IPX, NetBios,
   – OS buffers.
   – Etc.
• Socket connections are established with a few simple C
  commands, but there‟s a lot going on under the surface.
  That‟s covered in CS 514 or 519, not here.

• Sockets are abstractions of connections or pipes between
  two different computers. They can be read from and
  written to without worrying about issues like flow control,
  byte ordering, network routing, error control, retries, etc.
  For the programmer, within limits, they just “work.”

• Those limits include the following possibilities:
   – Connections fail due to network conditions.
   – The client or server on the other end sends “erroneous” data.
   – There are different versions of protocols or applications.
• The basic socket primitives for TCP connections (the only
  kind we‟re going to discuss) are the following:
   –   Open: prepare a socket for a connection
   –   Listen: assign a socket to listen at a TCP port
   –   Accept: wait until a incoming connection arrives
   –   Connect: connect to a given server and port
   –   Send: send data through the socket
   –   Recv: receive data from the socket
   –   Close: close the socket (and its connection)

       Different languages may implement these in different
       ways, but the basics are the same in C, Java or the MFC
       libraries (CSocket).
• C socket programming is rather low-level and ugly. In this
  lecture we will use a Socket class that I wrote to abstract
  away the ugly details.

• As an example of a client program that uses sockets, I
  wrote a simple web browser that requests an arbitrary
  document from a server and cout‟s the result to the screen.

• (Note that a proper web browser is a lot more complicated
  than this and would implement better handling of various
  issues, among them HTTP header parsing.)
• Server programs are the mirror image of the client code
  that we‟ve seen. The server would look like this:

  Socket s;
  s.Listen (80);
  Socket* p = s.Accept();           // Blocking call
  p->Recv();      // etc.
  p->Send();      // etc.
  delete p;

• A typical server would enclose the Accept() call in a loop
  and either spawn a thread for each incoming connection or
  pass requests off to a queue.
Socket s;
s.Listen (80);
Socket* p = NULL;

while (true) {
  p = s.Accept();
  EnQueue (p);

    if (some_condition) {
        break;      // Exit server loop
• The assumption here is that some other thread would
  handle the queued socket and delete it when finished.
           Threads and synchronization

• Threads are a very important topic in C++ programming.
  You aren‟t a “real” programmer until you‟ve had to deal
  with the problems of writing multi-threaded applications.

• However, the same problems that we were having with
  GUI‟s occur with threads: there are no standard library
  interfaces tha define thread creation and use.

• There are several different thread “packages” the one can
  use: C-threads, MFC, Win32, etc. Most OS‟s implement
  thread support in different proprietary ways, if at all.
• Traditionally, each program (or “process”) on a system
  would have only one line of execution (or “thread”). Each
  program would begin, process data and eventually exit.
  Each process would have its own virtual address space and
  its own heap, its own stack and its own set of global and
  static variables.

• However, most OS‟s allow multiple threads of execution
  per process, which share the same heap (usually) and the
  same global variables.

• Each thread has its own stack, however, which makes
  sense because each thread enters and exits functions
  independently from the others.
• Programs with more than one thread are called “multi-
  threaded.” Examples of multithreaded applications
   – Windows Explorer (the desktop process)
   – Any internet server (obviously)
   – Netscape (opens a thread per connection)
   – Microsoft Powerpoint (who knows?)
   – Most GUI applications (to provide fast redraws while
     performing background processing)
   – Games (different threads might control different aspects
     of the computer‟s AI)
   – etc.
• So how do threads work? This is their conceptual interface
  (provided in some form by most thread packages):
   – StartThread: spawn a new thread in a given function
     and initialize it with the given data (more on this in a
   – SuspendThread: put the thread to sleep
   – ResumeThread: wake the thread up
   – SetPriority: change the priority of the thread

• “Priority” refers to the importance of the specific thread in
  the OS‟s scheduler. In a pre-emptively multitasking
  environment, this determines how much CPU time the
  thread gets with respect to other processes and threads.
• The complicated material here is in the StartThread
  function (which in different OS‟s is called CreateThread,
  _beginthread, etc.)

• This function is usually defined as follows:
  StartThread (THREAD_FXN*, void*);

• THREAD_FXN would be a pointer to a function:
  typedef int (*THREAD_FXN) (void*);

• So one would call the function as follows:

DataStructure* p = new DataStructure();
StartThread (RunServer, (void*) p);
• The call would return immediately and a new thread would
  start executing in the following function:

int RunServer (void* pData) {
  DataStructure* p = (DataStructure*) pData;

• It is important to note that no assumptions can be made
  about the scheduling of the different threads within a
  process. Regardless of priorities, one can never assume
  that an instruction in one thread will be executed before an
  instruction in another thread.
• A corollary to this observation is that once multiple threads
  are running within a process, no assumptions can be made
  about the values of global objects or dynamically allocated
  memory referenced by global or shared pointers.

• For example, if several web server threads share a common
  pointer to a linked list of cached DNS entries, then all of
  them could conceivably be reading, inserting or deleting
  nodes at the same time.

• The dangers of this situation are obvious.

• An even simpler case can cause problems with multi-
  threaded applications. Imagine a global integer x with an
  increment function:

  void IncrementX() { x ++; }

• While this might seem to be an atomic operation, it
  actually isn‟t. Computers don‟t understand C++ natively;
  the C++ passes through a compiler and gets turned into
  machine code.

• IncrementX() looks like this when compiled in debug
  mode with VC++ 6.0:
004016A0   push   ebp
004016A1   mov    ebp,esp
004016A3   sub    esp,40h
004016A6   push   ebx
004016A7   push   esi
004016A8   push   edi
004016A9   mov    eax,[x (00415414)]
004016AE   add    eax,1
004016B1   mov    [x (00415414)],eax
004016B6   pop    edi
004016B7   pop    esi
004016B8   pop    ebx
004016B9   mov    esp,ebp
004016BB   pop    ebp
004016BC   ret
• The highlighted instructions are the important ones:

004016A9      mov               eax,[x (00415414)]
004016AE      add               eax,1
004016B1      mov               [x (00415414)],eax

• What could happen in a multithreaded application is the
   – Thread 1 calls IncrementX() and executes:
      004016AE          // The value in EAX is now x + 1
   – Thread 2 calls IncrementX() and returns. x is now x + 1.
   – Thread 1 executes
      004016B1          // x is now x + 1
   – So two calls to IncrementX only incremented x once!!
• Clearly we need a solution to this problem before we can
  write multithreaded applications. That solution is called

• Synchronization consists of using objects called “mutex
  locks” (mutex == mutual exclusion). Other types of
  objects can be used (such as semaphores), but we‟ll
  concentrate on objects that exist in common C++ libraries.

• Mutex objects have three basic interface functions:
   – Create: in Win32, CreateMutex()
   – Wait: in Win32, WaitForSingleObject()
   – Signal: in Win32, ReleaseMutex()
• The best way to think of mutexes is to conceive of them as
  boolean values with two possible states: „wait‟ and
  „signal.‟ Mutexes are initialized to the „signal‟ state.

• Wait() blocks until the state of the mutex is „signal‟, then
  sets it to „wait‟ and returns.

• Signal() sets the mutex back to the „signal‟ state, allowing
  another waiting thread to be woken up.

• So if we surround our x++ instruction with a Wait and a
  signal, only one thread can execute that instruction at a
Mutex m;

void IncrementX() {
  x ++;           // Critical section

• Only one thread will be able to enter the “critical section”
  at a time, so there‟s no danger of incrementing x

• Locking strategies may be arbitrarily complex. There are
  many “special case” algorithms like “readers and writers.”
• These more complex algorithms are used for different
  kinds of problems.

• R&W, for example, applies to the case where reads and
  writes of a resource take a long time, so it is desirable to
  allow readers to access the resource at the same time and
  only be locked out in the case of concurrent writer.

• In database systems, for example, this type of problem is
  solved with a “lock manager” that assigns read and write
  locks upon request, and has the right to deny them if the
  system‟s conditions do not allow their concession.

Description: Ai Factory Scheduling Multiple Problem Formulations document sample