aboutsummaryrefslogtreecommitdiff
path: root/external/glim/test_cbcoro.cc
blob: f47cdac39dece69c102bdac616b0cc0fbe6c27f2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// http://en.wikipedia.org/wiki/Setcontext; man 3 makecontext; man 2 getcontext
// http://www.boost.org/doc/libs/1_53_0/libs/context/doc/html/index.html
// g++ -std=c++11 -O1 -Wall -g test_cbcoro.cc -pthread && ./a.out

#include <glim/exception.hpp>
#include <glim/NsecTimer.hpp>

#include "cbcoro.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // sleep
#include <string.h> // strerror
#include <errno.h>
#include <functional>
using std::function;
#include <thread>
#include <memory>
using std::shared_ptr; using std::make_shared;
#include <string>
using std::string; using std::to_string;
#include <iostream>
using std::cout; using std::endl;

/** A typical remote service with callback. */
void esDelete (int frople, std::function<void(int)> cb) {
  std::thread th ([cb,frople]() {
    cout << "esDelete: sleeping for a second" << endl;
    std::this_thread::sleep_for (std::chrono::seconds (1));
    cb (frople);
  }); th.detach();
}

struct RemoveFroples: public glim::CBCoro {
  const char* _argument;
  RemoveFroples (const char* argument): _argument (argument) {
    cout << "RF: constructor" << endl;
  }
  virtual ~RemoveFroples() {puts ("~RemoveFroples");}
  virtual void run() override {
    for (int i = 1; i <= 4; ++i) {
      cout << "RF: Removing frople " << i << "..." << endl;
      int returnedFrople = 0;
      yieldForCallback ([this,i,&returnedFrople]() {
        if (i != 2) {
          // Sometimes we use a callback.
          esDelete (i, [this,&returnedFrople](int frople) {
            cout << "RF,CB: frople " << frople << "." << endl;
            returnedFrople = frople;
            invokeFromCallback();
          });
        } else {
          // Sometimes we don't use a callback.
          returnedFrople = 0;
          invokeFromCallback();
        }
      });
      cout << "RF: Returned from callback; _returnTo is: " << (intptr_t) _returnTo << "; frople " << returnedFrople << endl;
    }
    cout << "RF: finish! _returnTo is: " << (intptr_t) _returnTo << endl;
  };
};

int main() {
  glim::cbCoro ([](glim::CBCoro* cbcoro) {
    cout << "main: run1, thread " << std::this_thread::get_id() << endl;  // Runs on the `main` thread.
    cbcoro->yieldForCallback ([&]() {
      std::thread callbackThread ([&]() {
        std::this_thread::sleep_for (std::chrono::seconds (4));
        cbcoro->invokeFromCallback();
      }); callbackThread.detach();
    });
    cout << "main: run2, thread " << std::this_thread::get_id() << endl;  // Runs on the `callbackThread`.
  });

  (new RemoveFroples ("argument"))->start();
  cout << "main: returned from RemoveFroples" << endl;

  glim::NsecTimer timer; const int ops = RUNNING_ON_VALGRIND ? 999 : 9999;
  for (int i = 0; i < ops; ++i) glim::cbCoro ([](glim::CBCoro* cbcoro) {});
  double speedEmpty = ops / timer.sec();
  timer.restart();
  for (int i = 0; i < ops; ++i) glim::cbCoro ([](glim::CBCoro* cbcoro) {cbcoro->yieldForCallback ([&]() {cbcoro->invokeFromCallback();});});
  double speedImmediate = ops / timer.sec();

  sleep (5);
  cout << "speed: empty: " << speedEmpty << " o/s" << endl;
  cout << "speed: immediate: " << speedImmediate << " o/s" << endl;
  return 0;
}