/*******************************************************************************/ /* * File name: MThread.c * * Synopsis: This program shows how to use threads in a MIL application and * synchronize them with event. It creates 4 processing threads that * are used to work in 4 different regions of a display buffer. * * Thread usage: * - The main thread starts a processing thread in each of the 4 different * quarters of a display buffer. The main thread then waits for a key to * be pressed to stop them. * - The top-left and bottom-left threads work in a loop, as follows: the * top-left thread adds a constant to its buffer, then sends an event to * the bottom-left thread. The bottom-left thread waits for the event * from the top-left thread, rotates the top-left buffer image, then sends an * event to the top-left thread to start a new loop. * - The top-right and bottom-right threads work the same way as the * top-left and bottom-left threads, except that the bottom-right thread * performs an edge detection operation, rather than a rotation. * * Note : Under MIL-Lite, the threads will do graphic annotations instead. */ #include #include #include /* Local defines. */ #define IMAGE_FILE M_IMAGE_PATH MIL_TEXT("Bird.mim") #define IMAGE_WIDTH 256L #define IMAGE_HEIGHT 240L #define STRING_LENGHT_MAX 40 #define STRING_POS_X 10 #define STRING_POS_Y 220 #define DRAW_RADIUS_NUMBER 5 #define DRAW_RADIUS_STEP 10 #define DRAW_CENTER_POSX 196 #define DRAW_CENTER_POSY 180 /* Function prototypes. */ unsigned long MFTYPE TopThread(void *TPar); unsigned long MFTYPE BotLeftThread(void *TPar); unsigned long MFTYPE BotRightThread(void *TPar); void BalanceThreadScheduling(); /* Thread parameters structure. */ typedef struct ThreadParam { MIL_ID Id; MIL_ID System; MIL_ID SrcImage; MIL_ID DstImage; MIL_ID ReadyEvent; MIL_ID DoneEvent; long NumberOfIteration; long Radius; long Exit; struct ThreadParam *SlaveThreadParam; } THREAD_PARAM; /* Main function: */ /* -------------- */ void main(void) { MIL_ID MilApplication, /* Application identifier. */ MilSystem, /* System identifier. */ MilDisplay, /* Display identifier. */ MilImage; /* Image buffer identifiers. */ THREAD_PARAM TParTopLeft, /* Parameters passed to top left thread. */ TParBotLeft, /* Parameters passed to bottom left thread. */ TParTopRight, /* Parameters passed to top right thread. */ TParBotRight; /* Parameters passed to bottom right thread. */ /* Allocate defaults. */ MappAllocDefault(M_SETUP, &MilApplication, &MilSystem, &MilDisplay, M_NULL, M_NULL); /* Allocate and display the main image buffer. */ MbufAlloc2d(MilSystem, IMAGE_WIDTH*2, IMAGE_HEIGHT*2, 8+M_UNSIGNED, M_IMAGE+M_PROC+M_DISP, &MilImage); MbufClear(MilImage, 0); MdispSelect(MilDisplay,MilImage); /* Allocate a destination child buffer for each thread. */ MbufChild2d(MilImage, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, &TParTopLeft.SrcImage); MbufChild2d(MilImage, 0, IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_HEIGHT, &TParBotLeft.DstImage); MbufChild2d(MilImage, IMAGE_WIDTH, 0, IMAGE_WIDTH, IMAGE_HEIGHT, &TParTopRight.SrcImage); MbufChild2d(MilImage, IMAGE_WIDTH, IMAGE_HEIGHT,IMAGE_WIDTH, IMAGE_HEIGHT, &TParBotRight.DstImage); /* Allocate synchronization events. */ MthrAlloc(MilSystem, M_EVENT, M_DEFAULT, M_NULL, M_NULL, &TParTopLeft.DoneEvent); MthrAlloc(MilSystem, M_EVENT, M_DEFAULT, M_NULL, M_NULL, &TParBotLeft.DoneEvent); MthrAlloc(MilSystem, M_EVENT, M_DEFAULT, M_NULL, M_NULL, &TParTopRight.DoneEvent); MthrAlloc(MilSystem, M_EVENT, M_DEFAULT, M_NULL, M_NULL, &TParBotRight.DoneEvent); /* Intitalize remaining thread parameters. */ TParTopLeft.System = MilSystem; TParTopLeft.DstImage = TParTopLeft.SrcImage; TParTopLeft.ReadyEvent = TParBotLeft.DoneEvent; TParTopLeft.NumberOfIteration = 0; TParTopLeft.Radius = 0; TParTopLeft.Exit = 0; TParTopLeft.SlaveThreadParam = &TParBotLeft; TParBotLeft.System = MilSystem; TParBotLeft.SrcImage = TParTopLeft.DstImage; TParBotLeft.ReadyEvent = TParTopLeft.DoneEvent; TParBotLeft.NumberOfIteration = 0; TParBotLeft.Radius = 0; TParBotLeft.Exit = 0; TParBotLeft.SlaveThreadParam = 0; TParTopRight.System = MilSystem; TParTopRight.DstImage = TParTopRight.SrcImage; TParTopRight.ReadyEvent = TParBotRight.DoneEvent; TParTopRight.NumberOfIteration = 0; TParTopRight.Radius = 0; TParTopRight.Exit = 0; TParTopRight.SlaveThreadParam = &TParBotRight; TParBotRight.System = MilSystem; TParBotRight.SrcImage = TParTopRight.DstImage; TParBotRight.ReadyEvent = TParTopRight.DoneEvent; TParBotRight.NumberOfIteration = 0; TParBotRight.Radius = 0; TParBotRight.Exit = 0; TParBotRight.SlaveThreadParam = 0; /* Initialize the top buffers to process. */ MbufLoad(IMAGE_FILE, TParTopLeft.SrcImage); MbufLoad(IMAGE_FILE, TParTopRight.SrcImage); /* Start the 4 threads. */ MthrAlloc(M_DEFAULT, M_THREAD, M_DEFAULT, &TopThread , &TParTopLeft, &TParTopLeft.Id); MthrAlloc(M_DEFAULT, M_THREAD, M_DEFAULT, &BotLeftThread , &TParBotLeft, &TParBotLeft.Id); MthrAlloc(M_DEFAULT, M_THREAD, M_DEFAULT, &TopThread , &TParTopRight, &TParTopRight.Id); MthrAlloc(M_DEFAULT, M_THREAD, M_DEFAULT, &BotRightThread, &TParBotRight, &TParBotRight.Id); /* Set events to start operation of top left and top right threads. */ MthrControl(TParTopLeft.ReadyEvent, M_EVENT_SET, M_SIGNALED); MthrControl(TParTopRight.ReadyEvent, M_EVENT_SET, M_SIGNALED); /* Report that the threads are started and wait for a key press to stop them. */ printf("\nMULTI-THREADING:\n"); printf("----------------\n\n"); printf("4 threads running...\n"); printf("Press to stop.\n\n"); getch(); /* Signal the threads to exit. */ TParTopLeft.Exit = 1; TParTopRight.Exit = 1; /* Wait for all threads to terminate. */ MthrWait(TParTopLeft.Id , M_THREAD_END_WAIT, M_NULL); MthrWait(TParBotLeft.Id , M_THREAD_END_WAIT, M_NULL); MthrWait(TParTopRight.Id, M_THREAD_END_WAIT, M_NULL); MthrWait(TParBotRight.Id, M_THREAD_END_WAIT, M_NULL); /* Print statistics. */ printf("Top left iterations done: %4ld.\n", TParTopLeft.NumberOfIteration); printf("Bottom left iterations done: %4ld.\n", TParBotLeft.NumberOfIteration); printf("Top right iterations done: %4ld.\n", TParTopRight.NumberOfIteration); printf("Bottom right iterations done: %4ld.\n", TParBotRight.NumberOfIteration); printf("Press to end.\n\n"); getch(); /* Free threads. */ MthrFree(TParTopLeft.Id); MthrFree(TParBotLeft.Id); MthrFree(TParTopRight.Id); MthrFree(TParBotRight.Id); /* Free events. */ MthrFree(TParTopLeft.DoneEvent); MthrFree(TParBotLeft.DoneEvent); MthrFree(TParTopRight.DoneEvent); MthrFree(TParBotRight.DoneEvent); /* Free buffers. */ MbufFree(TParTopLeft.SrcImage); MbufFree(TParTopRight.SrcImage); MbufFree(TParBotLeft.DstImage); MbufFree(TParBotRight.DstImage); MbufFree(MilImage); /* Free defaults. */ MappFreeDefault(MilApplication, MilSystem, MilDisplay, M_NULL, M_NULL); } /* Top left and top right threads' function (Add an offset): */ /* --------------------------------------------------------- */ unsigned long MFTYPE TopThread(void *ThreadParameters) { THREAD_PARAM *TPar = (THREAD_PARAM *)ThreadParameters; MIL_TEXT_CHAR Text[STRING_LENGHT_MAX]; while (!TPar->Exit) { /* Wait for bottom ready event before proceeding. */ MthrWait(TPar->ReadyEvent, M_EVENT_WAIT, M_NULL); /* If processing available, Add a constant to the image. */ #if (!M_MIL_LITE) { MimArith(TPar->SrcImage, 5L, TPar->DstImage, M_ADD_CONST); } /* else draw a filled rectangle. */ #else { TPar->Radius = TPar->SlaveThreadParam->Radius = (TPar->NumberOfIteration % DRAW_RADIUS_NUMBER) * DRAW_RADIUS_STEP; if (TPar->Radius == 0) MbufLoad(IMAGE_FILE, TPar->DstImage); MgraColor(M_DEFAULT, 0xff); MgraRectFill(M_DEFAULT, TPar->DstImage, DRAW_CENTER_POSX - TPar->Radius, DRAW_CENTER_POSY - TPar->Radius, DRAW_CENTER_POSX + TPar->Radius, DRAW_CENTER_POSY + TPar->Radius); } #endif /* Increment iteration count and draw text. */ TPar->NumberOfIteration++; MgraColor(M_DEFAULT, 0xFF); MOs_ltoa(TPar->NumberOfIteration, Text, 10); MgraText(M_DEFAULT, TPar->DstImage, STRING_POS_X, STRING_POS_Y, Text); /* Signal to the bottom thread that the first part of processing is complete. */ MthrControl(TPar->DoneEvent, M_EVENT_SET, M_SIGNALED); /* For a smoother display effect, optional yield to the other threads. */ BalanceThreadScheduling(); } /* Require the bottom thread to exit. */ TPar->SlaveThreadParam->Exit = 1; /* Signal the bottom thread end to wake up. */ MthrControl(TPar->DoneEvent, M_EVENT_SET, M_SIGNALED); return(1L); } /* Bottom-left thread function (Rotate): */ /* ------------------------------------- */ unsigned long MFTYPE BotLeftThread(void *ThreadParameters) { THREAD_PARAM *TPar = (THREAD_PARAM *)ThreadParameters; MIL_TEXT_CHAR Text[STRING_LENGHT_MAX]; while (!TPar->Exit) { /* Wait for the event in top-left function to be ready before proceeding. */ MthrWait(TPar->ReadyEvent, M_EVENT_WAIT, M_NULL); /* If processing available, add a constant to the image. */ #if (!M_MIL_LITE) { MimRotate(TPar->SrcImage, TPar->DstImage, (TPar->NumberOfIteration*5)%360, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_NEAREST_NEIGHBOR+M_OVERSCAN_CLEAR); } /* else copy the top image and draw a filled circle. */ #else { MbufCopy(TPar->SrcImage,TPar->DstImage); MgraColor(M_DEFAULT, 0x80); MgraArcFill(M_DEFAULT, TPar->DstImage, DRAW_CENTER_POSX, DRAW_CENTER_POSY, TPar->Radius, TPar->Radius, 0, 360); } #endif /* Print iteration count and draw a circle. */ TPar->NumberOfIteration++; MgraColor(M_DEFAULT, 0xFF); MOs_ltoa(TPar->NumberOfIteration, Text, 10); MgraText(M_DEFAULT, TPar->DstImage, STRING_POS_X, STRING_POS_Y, Text); /* Signal to the top thread that the last part of the processing is complete. */ MthrControl(TPar->DoneEvent, M_EVENT_SET, M_SIGNALED); /* For a smoother display effect, optional yield to the other threads. */ BalanceThreadScheduling(); } return(1L); } /* Bottom-right thread function (Edge Detect): */ /* ------------------------------------------- */ unsigned long MFTYPE BotRightThread(void *ThreadParameters) { THREAD_PARAM *TPar = (THREAD_PARAM *)ThreadParameters; MIL_TEXT_CHAR Text[STRING_LENGHT_MAX]; while (!TPar->Exit) { /* Wait for the event in top-right function to be ready before proceeding. */ MthrWait(TPar->ReadyEvent, M_EVENT_WAIT, M_NULL); /* If processing available, add a constant to the image. */ #if (!M_MIL_LITE) { MimConvolve(TPar->SrcImage, TPar->DstImage, M_EDGE_DETECT); } /* else copy the top image and draw a filled circle. */ #else { MbufCopy(TPar->SrcImage,TPar->DstImage); MgraColor(M_DEFAULT, 0x40); MgraArcFill(M_DEFAULT, TPar->DstImage, DRAW_CENTER_POSX, DRAW_CENTER_POSY, TPar->Radius/2, TPar->Radius/2, 0, 360); } #endif /* Increment iteration count and draw text. */ TPar->NumberOfIteration++; MgraColor(M_DEFAULT, 0xFF); MOs_ltoa(TPar->NumberOfIteration, Text, 10); MgraText(M_DEFAULT, TPar->DstImage, STRING_POS_X, STRING_POS_Y, Text); /* Signal to the top thread that the last part of the processing is complete. */ MthrControl(TPar->DoneEvent, M_EVENT_SET, M_SIGNALED); /* For a smoother display effect, optional yield to the other threads. */ BalanceThreadScheduling(); } return(1L); } /* Thread Scheduling Adjustment Handling */ /* ------------------------------------- */ /* */ /* This function is used to give a smoother display effect. It balances the number of */ /* iterations of each thread by yielding its time slice frequently. */ /* */ /* NOTE: In some situations, Windows adjusts the priority or quantum value of threads. */ /* The intent of these adjustments is to improve system throughput and */ /* responsiveness. However, like any scheduling algorithms these adjustments are */ /* not perfect, and they might not be beneficial to all applications. The */ /* following function is introduced to minimize the impact of thread scheduling */ /* adjustment as performed by Windows. Refer to the "Inside Windows NT */ /* (Second Edition, Microsoft Press, 1998, ISBN 1-57231-677-2)" manual for more */ /* details on thread scheduling under Windows. */ void BalanceThreadScheduling() { Sleep(0); }