Last active
June 23, 2022 21:29
-
-
Save poseidon4o/3f97c84b2d505682aaa92fcda9b749a1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <mutex> | |
#include <vector> | |
#include <thread> | |
#include <condition_variable> | |
/// Size of the render target in console characters | |
const int WIDTH = 100; | |
const int HEIGHT = 30; | |
/// Render target, render threads write next frame here | |
/// 1 extra in width for terminating '\0' | |
char canvas[HEIGHT][WIDTH + 1]; | |
/// Context for the "simulation" part of the program | |
struct AnimationContext { | |
std::vector<int> lines = {0, 10, 20, 30, 40, 50}; ///< Positions of the lines | |
bool working = true; ///< Flag used to control until when the animation thread will run | |
std::mutex animLock; ///< Lock used to protect #working and be used for #cvar | |
std::condition_variable cvar; ///< Used to signal the thread to when it needs to exit | |
}; | |
void animationThread(AnimationContext &ctx) { | |
while (true) { | |
for (int &pos : ctx.lines) { | |
pos = (pos + 1) % WIDTH; | |
} | |
std::unique_lock<std::mutex> lock(ctx.animLock); | |
ctx.cvar.wait_for(lock, std::chrono::milliseconds(30), [&ctx] () { | |
return !ctx.working; | |
}); | |
if (!ctx.working) { | |
break; | |
} | |
} | |
puts("animationThread thread exiting"); | |
} | |
/// Shared context for all render threads | |
struct RenderContext { | |
AnimationContext &animationData; ///< Reference to the shared animation data, not null for render threads | |
bool working = true; ///< Used to signal when render threads need to exit | |
std::mutex renderLock; ///< The lock protecting the #working flag | |
}; | |
void renderThread(RenderContext &ctx, int index, int count) { | |
const int perThread = HEIGHT / count; | |
const int start = perThread * index; | |
const int end = index == count - 1 ? HEIGHT : start + perThread; | |
while (true) { | |
const std::vector<int> ¤t = ctx.animationData.lines; | |
for (int r = start; r < end; r++) { | |
for (int c = 0; c < WIDTH; c++) { | |
canvas[r][c] = ' '; | |
for (int l = 0; l < current.size(); l++) { | |
if (current[l] == c) { | |
canvas[r][c] = '|'; | |
} | |
} | |
} | |
canvas[r][WIDTH] = '\0'; | |
} | |
std::lock_guard<std::mutex> lock(ctx.renderLock); | |
if (!ctx.working) { | |
break; | |
} | |
} | |
printf("renderer thread %d exiting\n", index); | |
} | |
/// Context for the blitting thread, | |
struct BlitContext { | |
bool working = true; ///< Flag used to signal when blit thread needs to exit | |
std::mutex blitLock; ///< Lock protecting #working flag | |
}; | |
#include "Windows.h" | |
void blitThread(BlitContext &ctx) { | |
const HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE); | |
const COORD topLeft = {0, 0}; | |
while (true) { | |
/// reset cursor location to top left corner | |
SetConsoleCursorPosition(console, topLeft); | |
for (int r = 0; r < HEIGHT; r++) { | |
puts(canvas[r]); | |
} | |
std::lock_guard<std::mutex> lock(ctx.blitLock); | |
if (!ctx.working) { | |
break; | |
} | |
} | |
puts("blit thread exiting"); | |
} | |
int main() { | |
AnimationContext animData; | |
RenderContext renderData = {animData}; | |
BlitContext blitData; | |
std::thread blitWorker(blitThread, std::ref(blitData)); | |
std::thread animationWorker(animationThread, std::ref(animData)); | |
std::vector<std::thread> renderWorkers; | |
const int renderThreadCount = 4; | |
for (int c = 0; c < renderThreadCount; c++) { | |
renderWorkers.push_back(std::thread(renderThread, std::ref(renderData), c, renderThreadCount)); | |
} | |
// run the for 10 minutes | |
std::this_thread::sleep_for(std::chrono::minutes(10)); | |
// stop blit thread | |
{ | |
std::lock_guard<std::mutex> lock(blitData.blitLock); | |
blitData.working = false; | |
} | |
blitWorker.join(); | |
// stop animation thread | |
{ | |
std::lock_guard<std::mutex> lock(animData.animLock); | |
animData.working = false; | |
} | |
animData.cvar.notify_all(); | |
animationWorker.join(); | |
// stop all renderer threads | |
{ | |
std::lock_guard<std::mutex> lock(renderData.renderLock); | |
renderData.working = false; | |
} | |
for (int c = 0; c < renderThreadCount; c++) { | |
renderWorkers[c].join(); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment