Skip to content

Instantly share code, notes, and snippets.

@heiner
Last active June 17, 2020 16:17
Show Gist options
  • Select an option

  • Save heiner/671f1c53406cecc7987f067c7f34a335 to your computer and use it in GitHub Desktop.

Select an option

Save heiner/671f1c53406cecc7987f067c7f34a335 to your computer and use it in GitHub Desktop.
cmake_minimum_required(VERSION 3.10)
project(cppgen VERSION 0.0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
link_libraries(pthread)
add_executable(cppgen cppgen.cc)
add_executable(cppgen-dup2 cppgen-dup2.cc)
add_executable(cppgen-lock cppgen-lock.cc)
add_executable(cppgen-uc cppgen-uc.cc)
add_executable(cppnogen cppnogen.cc)
add_executable(cppgen-fc cppgen-fc.cc)
target_include_directories(cppgen-fc PRIVATE /Users/hnr/src/deboost.context/include)
target_link_options(cppgen-fc PRIVATE /Users/hnr/src/deboost.context/build/libfcontext.a)
// Cf. https://github.com/python/cpython/blob/2.7/Demo/threads/Generator.py
#include <termios.h>
#include <unistd.h>
#include <cassert>
#include <cstdio>
#include <chrono>
#include <iostream>
#include <thread>
int max = 4;
struct tty_restore {
tty_restore(int fd = STDIN_FILENO) : fd(fd) {
tcgetattr(fd, &old);
tty = old;
tty.c_lflag &= ~ICANON;
tty.c_lflag &= ~ECHO;
set();
}
~tty_restore() { clear(); }
void set() { tcsetattr(fd, TCSANOW, &tty); }
void clear() { tcsetattr(fd, TCSANOW, &old); }
int fd;
struct termios old, tty;
};
struct Generator {
Generator() : std_stdout(dup(STDOUT_FILENO)) {
pipe(pipe_stdout);
pipe(pipe_lock);
}
~Generator() {
if (started) {
thread_.join();
restore_stdout();
}
for (int i = 0; i < 2; ++i) {
close(pipe_stdout[i]);
close(pipe_lock[i]);
}
}
template <class Function>
std::string start(Function f) {
assert(!started);
pipe_as_stdout();
thread_ = std::thread(f, this);
started = true;
return get();
}
char put() {
// Producer has done one step. Get one char from consumer.
char c = '\0';
std::cout << c;
std::cout.flush();
read(pipe_lock[0], &c, 1);
return c;
}
void end() {
// Producer is done.
std::cout << '\0';
std::cout.flush();
}
std::string get() {
std::string result(BUFSIZ, ' ');
ssize_t size;
size_t length = 0;
size_t remaining = result.length();
do {
if (remaining == 0) {
result.resize(2 * result.length());
remaining = result.length() - length;
}
size = read(pipe_stdout[0], static_cast<void *>(&(result[length])),
remaining);
assert(size > 0);
length += size;
remaining -= size;
} while (result[length - 1] != '\0');
result.resize(length - 1);
restore_stdout();
return result;
}
std::string send(char c) {
pipe_as_stdout();
write(pipe_lock[1], (void *)&c, 1);
return get();
}
void pipe_as_stdout() {
fflush(stdout);
assert(dup2(pipe_stdout[1], STDOUT_FILENO) != -1);
}
void restore_stdout() { assert(dup2(std_stdout, STDOUT_FILENO) != -1); }
std::thread thread_;
int pipe_stdout[2];
int std_stdout;
int pipe_lock[2];
bool started = false;
};
void mainloop(Generator *g) {
tty_restore r;
char input;
for (int i = 1; i < max; ++i) {
std::cout << " #" << i << " >>> ";
input = g->put();
std::cout << "Last input: " << static_cast<int>(input);
}
g->end();
}
int main(int argc, char **argv) {
{
if (argc > 1) max = std::stoi(argv[1]);
Generator g;
auto start = std::chrono::system_clock::now();
std::string output = g.start([](Generator *g) { mainloop(g); });
auto now = std::chrono::system_clock::now();
// std::cout << '(' << output << ')';
for (int i = 1; i < max; ++i) {
output = g.send(static_cast<char>(i));
if (i % 10000 == 0) {
now = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed = now - start;
std::cout << i << ": " << 1000.0 / elapsed.count() << " SPS"
<< std::endl;
start = now;
}
// std::cout << '(' << output << ')';
}
}
std::cout << std::endl << "All done." << std::endl;
}
// From tscmoo at
// https://gist.github.com/tscmoo/a182a44a7ec8f22a16fabce5a48f946a
#include <unistd.h>
#include <cassert>
#include <cstdio>
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
#include <fcontext/fcontext.h>
int max = 4;
#define STACK_SIZE (1 << 15) // 32KiB
struct Generator {
Generator() : stack(create_fcontext_stack(STACK_SIZE)) {
pipe(pipe_stdout);
pipe(pipe_lock);
out = fdopen(pipe_stdout[1], "w");
}
~Generator() {
fclose(out);
for (int i = 0; i < 2; ++i) {
close(pipe_stdout[i]);
close(pipe_lock[i]);
}
destroy_fcontext_stack(&stack);
}
fcontext_stack_t stack;
fcontext_t generatorcontext;
fcontext_t returncontext;
template <class Function>
std::string start(Function f) {
assert(!started);
std::function<void(fcontext_transfer_t)> innerf =
[&](fcontext_transfer_t t) {
returncontext = t.ctx;
f(this);
};
auto corofn = [](fcontext_transfer_t t) {
auto f = (std::function<void(fcontext_transfer_t)> *)t.data;
(*f)(t);
};
generatorcontext = make_fcontext(stack.sptr, stack.ssize,
(void (*)(fcontext_transfer_t))corofn);
fcontext_transfer_t t = jump_fcontext(generatorcontext, &innerf);
generatorcontext = t.ctx;
started = true;
return get();
}
char put() {
// Producer has done one step. Get one char from consumer.
char c;
end();
read(pipe_lock[0], &c, 1);
return c;
}
void end() {
fflush(out);
char c = '\0';
write(pipe_stdout[1], &c, 1);
fcontext_transfer_t t = jump_fcontext(returncontext, NULL);
returncontext = t.ctx;
}
std::string get() {
std::string result(BUFSIZ, ' ');
ssize_t size;
size_t length = 0;
size_t remaining = result.length();
do {
if (remaining == 0) {
result.resize(2 * result.length());
remaining = result.length() - length;
}
size = read(pipe_stdout[0], static_cast<void *>(&(result[length])),
remaining);
assert(size > 0);
length += size;
remaining -= size;
} while (result[length - 1] != '\0');
result.resize(length - 1);
return result;
}
std::string send(char c) {
write(pipe_lock[1], (void *)&c, 1);
jump_fcontext(generatorcontext, NULL);
return get();
}
int pipe_stdout[2];
int std_stdout;
int pipe_lock[2];
bool started = false;
FILE *out;
};
void mainloop(Generator *g) {
char input;
for (int i = 1; i < max; ++i) {
fprintf(g->out, " #%d >>> ", i);
input = g->put();
fprintf(g->out, "Last input: %i", static_cast<int>(input));
}
g->end();
}
int main(int argc, char **argv) {
{
if (argc > 1) max = std::stoi(argv[1]);
Generator g;
auto start = std::chrono::system_clock::now();
std::string output = g.start([](Generator *g) { mainloop(g); });
auto now = std::chrono::system_clock::now();
for (int i = 1; i < max; ++i) {
output = g.send(static_cast<char>(i));
if (i % 10000 == 0) {
now = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed = now - start;
std::cout << i << ": " << 1000.0 / elapsed.count() << " SPS"
<< std::endl;
start = now;
}
// std::cout << '(' << output << ')';
}
}
std::cout << std::endl << "All done." << std::endl;
}
// Cf. https://github.com/python/cpython/blob/2.7/Demo/threads/Generator.py
#include <unistd.h>
#include <cassert>
#include <cstdio>
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
int max = 4;
struct Generator {
Generator()
: prod_lock_(mu_, std::defer_lock), cons_lock_(mu_, std::defer_lock) {
pipe(pipe_stdout);
pipe(pipe_lock);
out = fdopen(pipe_stdout[1], "w");
}
~Generator() {
if (started) thread_.join();
fclose(out);
for (int i = 0; i < 2; ++i) {
close(pipe_stdout[i]);
close(pipe_lock[i]);
}
}
template <class Function>
std::string start(Function f) {
assert(!started);
prod_lock_.lock();
producer_ = true;
thread_ = std::thread(f, this);
started = true;
return get();
}
char put() {
// Producer has done one step. Get one char from consumer.
char c;
end();
prod_lock_.lock();
while (!producer_) {
cv_.wait(prod_lock_);
}
read(pipe_lock[0], &c, 1);
return c;
}
void end() {
char c = '\0';
write(pipe_stdout[1], &c, 1);
producer_ = false;
prod_lock_.unlock();
cv_.notify_one();
}
std::string get() {
cons_lock_.lock();
while (producer_) {
cv_.wait(cons_lock_);
}
std::string result(BUFSIZ, ' ');
ssize_t size;
size_t length = 0;
size_t remaining = result.length();
do {
if (remaining == 0) {
result.resize(2 * result.length());
remaining = result.length() - length;
}
size = read(pipe_stdout[0], static_cast<void *>(&(result[length])),
remaining);
assert(size > 0);
length += size;
remaining -= size;
} while (result[length - 1] != '\0');
result.resize(length - 1);
return result;
}
std::string send(char c) {
write(pipe_lock[1], (void *)&c, 1);
producer_ = true;
cons_lock_.unlock();
cv_.notify_one();
return get();
}
std::thread thread_;
int pipe_stdout[2];
int std_stdout;
int pipe_lock[2];
bool started = false;
FILE *out;
std::mutex mu_;
std::unique_lock<std::mutex> prod_lock_; // needs to persist.
std::unique_lock<std::mutex> cons_lock_; // needs to persist.
std::condition_variable cv_;
bool producer_;
};
void mainloop(Generator *g) {
char input;
for (int i = 1; i < max; ++i) {
fprintf(g->out, " #%d >>> ", i);
input = g->put();
fprintf(g->out, "Last input: %i", static_cast<int>(input));
}
g->end();
}
int main(int argc, char **argv) {
{
if (argc > 1) max = std::stoi(argv[1]);
Generator g;
auto start = std::chrono::system_clock::now();
std::string output = g.start([](Generator *g) { mainloop(g); });
auto now = std::chrono::system_clock::now();
// std::cout << '(' << output << ')';
for (int i = 1; i < max; ++i) {
output = g.send(static_cast<char>(i));
if (i % 10000 == 0) {
now = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed = now - start;
std::cout << i << ": " << 1000.0 / elapsed.count() << " SPS"
<< std::endl;
start = now;
}
// std::cout << '(' << output << ')';
}
}
std::cout << std::endl << "All done." << std::endl;
}
// From tscmoo at
// https://gist.github.com/tscmoo/a182a44a7ec8f22a16fabce5a48f946a
#include <unistd.h>
#include <cassert>
#include <cstdio>
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
#if defined(__APPLE__)
#define _XOPEN_SOURCE 1
#endif
#include <ucontext.h>
int max = 4;
#define STACK_SIZE (1 << 15) // 32KiB
struct Generator {
Generator() {
pipe(pipe_stdout);
pipe(pipe_lock);
out = fdopen(pipe_stdout[1], "w");
}
~Generator() {
fclose(out);
for (int i = 0; i < 2; ++i) {
close(pipe_stdout[i]);
close(pipe_lock[i]);
}
}
std::vector<char> stack;
ucontext_t generatorcontext;
ucontext_t returncontext;
template <class Function>
std::string start(Function f) {
assert(!started);
stack.resize(STACK_SIZE);
getcontext(&generatorcontext);
generatorcontext.uc_stack.ss_sp = stack.data();
generatorcontext.uc_stack.ss_size = stack.size();
makecontext(&generatorcontext, (void (*)())(void (*)(Generator *))f, 1,
this);
swapcontext(&returncontext, &generatorcontext);
started = true;
return get();
}
char put() {
// Producer has done one step. Get one char from consumer.
char c;
end();
read(pipe_lock[0], &c, 1);
return c;
}
void end() {
fflush(out);
char c = '\0';
write(pipe_stdout[1], &c, 1);
swapcontext(&generatorcontext, &returncontext);
}
std::string get() {
std::string result(BUFSIZ, ' ');
ssize_t size;
size_t length = 0;
size_t remaining = result.length();
do {
if (remaining == 0) {
result.resize(2 * result.length());
remaining = result.length() - length;
}
size = read(pipe_stdout[0], static_cast<void *>(&(result[length])),
remaining);
assert(size > 0);
length += size;
remaining -= size;
} while (result[length - 1] != '\0');
result.resize(length - 1);
return result;
}
std::string send(char c) {
write(pipe_lock[1], (void *)&c, 1);
swapcontext(&returncontext, &generatorcontext);
return get();
}
int pipe_stdout[2];
int std_stdout;
int pipe_lock[2];
bool started = false;
FILE *out;
};
void mainloop(Generator *g) {
char input;
for (int i = 1; i < max; ++i) {
fprintf(g->out, " #%d >>> ", i);
input = g->put();
fprintf(g->out, "Last input: %i", static_cast<int>(input));
}
g->end();
}
int main(int argc, char **argv) {
{
if (argc > 1) max = std::stoi(argv[1]);
Generator g;
auto start = std::chrono::system_clock::now();
std::string output = g.start([](Generator *g) { mainloop(g); });
auto now = std::chrono::system_clock::now();
// std::cout << '(' << output << ')';
for (int i = 1; i < max; ++i) {
output = g.send(static_cast<char>(i));
if (i % 10000 == 0) {
now = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed = now - start;
std::cout << i << ": " << 1000.0 / elapsed.count() << " SPS"
<< std::endl;
start = now;
}
// std::cout << '(' << output << ')';
}
}
std::cout << std::endl << "All done." << std::endl;
}
// Cf. https://github.com/python/cpython/blob/2.7/Demo/threads/Generator.py
#include <unistd.h>
#include <cassert>
#include <cstdio>
#include <chrono>
#include <iostream>
#include <thread>
int max = 4;
struct Generator {
Generator() {
pipe(pipe_stdout);
pipe(pipe_lock);
out = fdopen(pipe_stdout[1], "w");
}
~Generator() {
if (started) thread_.join();
fclose(out);
for (int i = 0; i < 2; ++i) {
close(pipe_stdout[i]);
close(pipe_lock[i]);
}
}
template <class Function>
std::string start(Function f) {
assert(!started);
thread_ = std::thread(f, this);
started = true;
return get();
}
char put() {
// Producer has done one step. Get one char from consumer.
char c;
end();
read(pipe_lock[0], &c, 1);
return c;
}
void end() {
char c = '\0';
write(pipe_stdout[1], &c, 1);
}
std::string get() {
std::string result(BUFSIZ, ' ');
ssize_t size;
size_t length = 0;
size_t remaining = result.length();
do {
if (remaining == 0) {
result.resize(2 * result.length());
remaining = result.length() - length;
}
size = read(pipe_stdout[0], static_cast<void *>(&(result[length])),
remaining);
assert(size > 0);
length += size;
remaining -= size;
} while (result[length - 1] != '\0');
result.resize(length - 1);
return result;
}
std::string send(char c) {
write(pipe_lock[1], (void *)&c, 1);
return get();
}
std::thread thread_;
int pipe_stdout[2];
int std_stdout;
int pipe_lock[2];
bool started = false;
FILE *out;
};
void mainloop(Generator *g) {
char input;
for (int i = 1; i < max; ++i) {
fprintf(g->out, " #%d >>> ", i);
input = g->put();
fprintf(g->out, "Last input: %i", static_cast<int>(input));
}
g->end();
}
int main(int argc, char **argv) {
{
if (argc > 1) max = std::stoi(argv[1]);
Generator g;
auto start = std::chrono::system_clock::now();
std::string output = g.start([](Generator *g) { mainloop(g); });
auto now = std::chrono::system_clock::now();
// std::cout << '(' << output << ')';
for (int i = 1; i < max; ++i) {
output = g.send(static_cast<char>(i));
if (i % 10000 == 0) {
now = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed = now - start;
std::cout << i << ": " << 1000.0 / elapsed.count() << " SPS"
<< std::endl;
start = now;
}
// std::cout << '(' << output << ')';
}
}
std::cout << std::endl << "All done." << std::endl;
}
// Cf. https://github.com/python/cpython/blob/2.7/Demo/threads/Generator.py
#include <unistd.h>
#include <cassert>
#include <cstdio>
#include <chrono>
#include <iostream>
#include <thread>
int max = 4;
struct Generator {
Generator() {
pipe(pipe_stdout);
pipe(pipe_lock);
out = fdopen(pipe_stdout[1], "w");
}
~Generator() {
fclose(out);
for (int i = 0; i < 2; ++i) {
close(pipe_stdout[i]);
close(pipe_lock[i]);
}
}
std::string start() {
assert(!started);
started = true;
return get();
}
char put() {
// Producer has done one step. Get one char from consumer.
char c;
end();
return c;
}
void end() {
char c = '\0';
write(pipe_stdout[1], &c, 1);
}
std::string get() {
std::string result(BUFSIZ, ' ');
ssize_t size;
size_t length = 0;
size_t remaining = result.length();
do {
if (remaining == 0) {
result.resize(2 * result.length());
remaining = result.length() - length;
}
size = read(pipe_stdout[0], static_cast<void *>(&(result[length])),
remaining);
assert(size > 0);
length += size;
remaining -= size;
} while (result[length - 1] != '\0');
result.resize(length - 1);
return result;
}
std::string send(char c) { return get(); }
int pipe_stdout[2];
int std_stdout;
int pipe_lock[2];
bool started = false;
FILE *out;
};
void iteration(Generator *g, int i) {
fprintf(g->out, " #%d >>> ", i);
fprintf(g->out, "Last input: %i", i);
g->end();
}
int main(int argc, char **argv) {
{
if (argc > 1) max = std::stoi(argv[1]);
Generator g;
auto start = std::chrono::system_clock::now();
iteration(&g, 1);
std::string output = g.start();
auto now = std::chrono::system_clock::now();
for (int i = 1; i < max; ++i) {
iteration(&g, i);
output = g.send(static_cast<char>(i));
if (i % 10000 == 0) {
now = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed = now - start;
std::cout << i << ": " << 1000.0 / elapsed.count() << " SPS"
<< std::endl;
start = now;
}
// std::cout << '(' << output << ')';
}
}
std::cout << std::endl << "All done." << std::endl;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment