Skip to content

Commit 4b45e14

Browse files
committed
Add standalone example with 3 processes
Added build instructions to README, but could use some more documentation and CMakeLists.txt file could be made less repetitive maybe introducing a mp_generate_cpp function that works like the capnp_generate_cpp function.
1 parent 17bfda9 commit 4b45e14

12 files changed

+440
-0
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,5 @@ install(FILES "include/mpgen.mk" DESTINATION "include")
9191

9292
install(EXPORT Multiprocess DESTINATION lib/cmake/Multiprocess)
9393

94+
add_subdirectory(example EXCLUDE_FROM_ALL)
9495
add_subdirectory(test EXCLUDE_FROM_ALL)

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ basis in this library to construct the event-loop necessary to service IPC reque
2727

2828
A simple interface description can be found at [test/mp/test/foo.capnp](test/mp/test/foo.capnp), implementation in [test/mp/test/foo.h](test/mp/test/foo.h), and usage in [test/mp/test/test.cpp](test/mp/test/test.cpp).
2929

30+
A more complete example can be found in [example](example/) and run with:
31+
32+
```sh
33+
make -C build example
34+
build/example/mpexample
35+
```
36+
3037
## Future directions
3138

3239
_libmultiprocess_ uses the [Cap'n Proto](https://capnproto.org) interface description language and protocol, but it could be extended or changed to use a different IDL/protocol like [gRPC](https://grpc.io). The nice thing about _Cap'n Proto_ compared to _gRPC_ and most other lower level protocols is that it allows interface pointers (_Services_ in gRPC parlance) to be passed as method arguments and return values, so object references and bidirectional requests work out of the box. Supporting a lower-level protocol would require writing adding maps and tracking code to proxy objects.

example/CMakeLists.txt

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Copyright (c) 2021 The Bitcoin Core developers
2+
# Distributed under the MIT software license, see the accompanying
3+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
add_custom_command(
6+
OUTPUT
7+
init.capnp.h
8+
init.capnp.c++
9+
init.capnp.proxy.h
10+
init.capnp.proxy-server.c++
11+
init.capnp.proxy-client.c++
12+
init.capnp.proxy-types.c++
13+
init.capnp.proxy-types.h
14+
COMMAND mpgen "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/init.capnp" "${CMAKE_SOURCE_DIR}/include" "${capnp_PREFIX}/include"
15+
DEPENDS init.capnp mpgen
16+
)
17+
18+
add_custom_command(
19+
OUTPUT
20+
calculator.capnp.h
21+
calculator.capnp.c++
22+
calculator.capnp.proxy.h
23+
calculator.capnp.proxy-server.c++
24+
calculator.capnp.proxy-client.c++
25+
calculator.capnp.proxy-types.c++
26+
calculator.capnp.proxy-types.h
27+
COMMAND mpgen "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/calculator.capnp" "${CMAKE_SOURCE_DIR}/include" "${capnp_PREFIX}/include"
28+
DEPENDS calculator.capnp mpgen
29+
)
30+
31+
add_executable(mpcalculator
32+
calculator.capnp.h
33+
calculator.capnp.c++
34+
calculator.capnp.proxy.h
35+
calculator.capnp.proxy-server.c++
36+
calculator.capnp.proxy-client.c++
37+
calculator.capnp.proxy-types.c++
38+
calculator.capnp.proxy-types.h
39+
calculator.cpp
40+
init.capnp.h
41+
init.capnp.c++
42+
init.capnp.proxy.h
43+
init.capnp.proxy-server.c++
44+
init.capnp.proxy-client.c++
45+
init.capnp.proxy-types.c++
46+
init.capnp.proxy-types.h
47+
printer.capnp.h
48+
printer.capnp.c++
49+
printer.capnp.proxy.h
50+
printer.capnp.proxy-server.c++
51+
printer.capnp.proxy-client.c++
52+
printer.capnp.proxy-types.c++
53+
printer.capnp.proxy-types.h
54+
)
55+
target_include_directories(mpcalculator PUBLIC
56+
${CAPNP_INCLUDE_DIRECTORY}
57+
${CMAKE_CURRENT_SOURCE_DIR}
58+
${CMAKE_CURRENT_BINARY_DIR}
59+
)
60+
target_link_libraries(mpcalculator PRIVATE CapnProto::capnp)
61+
target_link_libraries(mpcalculator PRIVATE CapnProto::capnp-rpc)
62+
target_link_libraries(mpcalculator PRIVATE CapnProto::kj)
63+
target_link_libraries(mpcalculator PRIVATE CapnProto::kj-async)
64+
target_link_libraries(mpcalculator PRIVATE Threads::Threads)
65+
target_link_libraries(mpcalculator PRIVATE multiprocess)
66+
set_target_properties(mpcalculator PROPERTIES
67+
CXX_STANDARD 17
68+
CXX_STANDARD_REQUIRED YES)
69+
70+
add_custom_command(
71+
OUTPUT
72+
printer.capnp.h
73+
printer.capnp.c++
74+
printer.capnp.proxy.h
75+
printer.capnp.proxy-server.c++
76+
printer.capnp.proxy-client.c++
77+
printer.capnp.proxy-types.c++
78+
printer.capnp.proxy-types.h
79+
COMMAND mpgen "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/printer.capnp" "${CMAKE_SOURCE_DIR}/include" "${capnp_PREFIX}/include"
80+
DEPENDS printer.capnp mpgen
81+
)
82+
83+
add_executable(mpprinter
84+
calculator.capnp.c++
85+
calculator.capnp.h
86+
calculator.capnp.proxy-client.c++
87+
calculator.capnp.proxy-server.c++
88+
calculator.capnp.proxy-types.c++
89+
calculator.capnp.proxy-types.h
90+
calculator.capnp.proxy.h
91+
init.capnp.h
92+
init.capnp.c++
93+
init.capnp.proxy.h
94+
init.capnp.proxy-server.c++
95+
init.capnp.proxy-client.c++
96+
init.capnp.proxy-types.c++
97+
init.capnp.proxy-types.h
98+
printer.capnp.h
99+
printer.capnp.c++
100+
printer.capnp.proxy.h
101+
printer.capnp.proxy-server.c++
102+
printer.capnp.proxy-client.c++
103+
printer.capnp.proxy-types.c++
104+
printer.capnp.proxy-types.h
105+
printer.cpp
106+
)
107+
target_include_directories(mpprinter PUBLIC
108+
${CAPNP_INCLUDE_DIRECTORY}
109+
${CMAKE_CURRENT_SOURCE_DIR}
110+
${CMAKE_CURRENT_BINARY_DIR}
111+
)
112+
target_link_libraries(mpprinter PRIVATE CapnProto::capnp)
113+
target_link_libraries(mpprinter PRIVATE CapnProto::capnp-rpc)
114+
target_link_libraries(mpprinter PRIVATE CapnProto::kj)
115+
target_link_libraries(mpprinter PRIVATE CapnProto::kj-async)
116+
target_link_libraries(mpprinter PRIVATE Threads::Threads)
117+
target_link_libraries(mpprinter PRIVATE multiprocess)
118+
set_target_properties(mpprinter PROPERTIES
119+
CXX_STANDARD 17
120+
CXX_STANDARD_REQUIRED YES)
121+
122+
add_executable(mpexample
123+
calculator.capnp.c++
124+
calculator.capnp.h
125+
calculator.capnp.proxy-client.c++
126+
calculator.capnp.proxy-server.c++
127+
calculator.capnp.proxy-types.c++
128+
calculator.capnp.proxy-types.h
129+
calculator.capnp.proxy.h
130+
init.capnp.c++
131+
init.capnp.h
132+
init.capnp.proxy-client.c++
133+
init.capnp.proxy-server.c++
134+
init.capnp.proxy-types.c++
135+
init.capnp.proxy-types.h
136+
init.capnp.proxy.h
137+
printer.capnp.h
138+
printer.capnp.c++
139+
printer.capnp.proxy.h
140+
printer.capnp.proxy-server.c++
141+
printer.capnp.proxy-client.c++
142+
printer.capnp.proxy-types.c++
143+
printer.capnp.proxy-types.h
144+
printer.h
145+
calculator.h
146+
example.cpp
147+
)
148+
target_include_directories(mpexample PUBLIC
149+
${CAPNP_INCLUDE_DIRECTORY}
150+
${CMAKE_CURRENT_SOURCE_DIR}
151+
${CMAKE_CURRENT_BINARY_DIR}
152+
)
153+
target_link_libraries(mpexample PRIVATE CapnProto::capnp)
154+
target_link_libraries(mpexample PRIVATE CapnProto::capnp-rpc)
155+
target_link_libraries(mpexample PRIVATE CapnProto::kj)
156+
target_link_libraries(mpexample PRIVATE CapnProto::kj-async)
157+
target_link_libraries(mpexample PRIVATE Threads::Threads)
158+
target_link_libraries(mpexample PRIVATE multiprocess)
159+
target_link_libraries(mpexample PRIVATE stdc++fs)
160+
set_target_properties(mpexample PROPERTIES
161+
CXX_STANDARD 17
162+
CXX_STANDARD_REQUIRED YES)
163+
164+
add_custom_target(example DEPENDS mpexample mpcalculator mpprinter)

example/calculator.capnp

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright (c) 2021 The Bitcoin Core developers
2+
# Distributed under the MIT software license, see the accompanying
3+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
@0xb67dbf34061180a9;
6+
7+
using Cxx = import "/capnp/c++.capnp";
8+
using Proxy = import "/mp/proxy.capnp";
9+
10+
$Proxy.include("calculator.h");
11+
12+
interface CalculatorInterface $Proxy.wrap("Calculator") {
13+
destroy @0 (context :Proxy.Context) -> ();
14+
solveEquation @1 (context :Proxy.Context, eqn: Text) -> ();
15+
}

example/calculator.cpp

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <calculator.h>
6+
#include <fstream>
7+
#include <init.capnp.h>
8+
#include <init.capnp.proxy-types.h>
9+
#include <init.h>
10+
#include <iostream>
11+
#include <memory>
12+
#include <mp/proxy-io.h>
13+
#include <printer.h>
14+
#include <stdexcept>
15+
16+
class CalculatorImpl : public Calculator
17+
{
18+
public:
19+
CalculatorImpl(std::unique_ptr<Printer> printer) : m_printer(std::move(printer)) {}
20+
void solveEquation(const std::string& eqn) override { m_printer->print("Wow " + eqn + ", that's a tough one.\n"); }
21+
std::unique_ptr<Printer> m_printer;
22+
};
23+
24+
class InitImpl : public Init
25+
{
26+
public:
27+
std::unique_ptr<Calculator> makeCalculator(std::unique_ptr<Printer> printer) override
28+
{
29+
return std::make_unique<CalculatorImpl>(std::move(printer));
30+
}
31+
};
32+
33+
void LogPrint(bool raise, std::string message)
34+
{
35+
if (raise) throw std::runtime_error(std::move(message));
36+
std::ofstream("debug.log", std::ios_base::app) << message << std::endl;
37+
}
38+
39+
int main(int argc, char** argv)
40+
{
41+
if (argc != 2) {
42+
std::cout << "Usage: mpcalculator <fd>\n";
43+
return 1;
44+
}
45+
mp::EventLoop loop("mpcalculator", LogPrint);
46+
int fd = std::stoi(argv[1]);
47+
std::unique_ptr<Init> init = std::make_unique<InitImpl>();
48+
mp::ServeStream<InitInterface>(loop, fd, *init);
49+
loop.loop();
50+
return 0;
51+
}

example/calculator.h

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef EXAMPLE_CALCULATOR_H
6+
#define EXAMPLE_CALCULATOR_H
7+
8+
#include <string>
9+
10+
class Calculator
11+
{
12+
public:
13+
virtual ~Calculator() = default;
14+
virtual void solveEquation(const std::string& eqn) = 0;
15+
};
16+
17+
#endif // EXAMPLE_CALCULATOR_H

example/example.cpp

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <filesystem>
6+
#include <fstream>
7+
#include <init.capnp.h>
8+
#include <init.capnp.proxy-types.h>
9+
#include <iostream>
10+
#include <mp/proxy-io.h>
11+
12+
namespace fs = std::filesystem;
13+
14+
auto Spawn(mp::EventLoop& loop, const std::string& process_argv0, const std::string& new_exe_name)
15+
{
16+
int pid;
17+
int fd = mp::SpawnProcess(pid, [&](int fd) -> std::vector<std::string> {
18+
fs::path path = process_argv0;
19+
path.remove_filename();
20+
path.append(new_exe_name);
21+
return {path.string(), std::to_string(fd)};
22+
});
23+
return std::make_tuple(mp::ConnectStream<InitInterface>(loop, fd), pid);
24+
}
25+
26+
void LogPrint(bool raise, std::string message)
27+
{
28+
if (raise) throw std::runtime_error(std::move(message));
29+
std::ofstream("debug.log", std::ios_base::app) << message << std::endl;
30+
}
31+
32+
int main(int argc, char** argv)
33+
{
34+
if (argc != 1) {
35+
std::cout << "Usage: mpexample\n";
36+
return 1;
37+
}
38+
39+
std::promise<mp::EventLoop*> promise;
40+
std::thread loop_thread([&] {
41+
mp::EventLoop loop("mpexample", LogPrint);
42+
{
43+
std::unique_lock<std::mutex> lock(loop.m_mutex);
44+
loop.addClient(lock);
45+
}
46+
promise.set_value(&loop);
47+
loop.loop();
48+
});
49+
mp::EventLoop* loop = promise.get_future().get();
50+
51+
auto [printer_init, printer_pid] = Spawn(*loop, argv[0], "mpprinter");
52+
auto [calc_init, calc_pid] = Spawn(*loop, argv[0], "mpcalculator");
53+
auto calc = calc_init->makeCalculator(printer_init->makePrinter());
54+
while (true) {
55+
std::string eqn;
56+
std::cout << "Enter the equation: ";
57+
std::getline(std::cin, eqn);
58+
calc->solveEquation(eqn);
59+
}
60+
calc.reset();
61+
calc_init.reset();
62+
mp::WaitProcess(calc_pid);
63+
printer_init.reset();
64+
mp::WaitProcess(printer_pid);
65+
return 0;
66+
}

example/init.capnp

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright (c) 2021 The Bitcoin Core developers
2+
# Distributed under the MIT software license, see the accompanying
3+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
@0xba5a7448664901b1;
6+
7+
using Cxx = import "/capnp/c++.capnp";
8+
using Proxy = import "/mp/proxy.capnp";
9+
using Calculator = import "calculator.capnp";
10+
using Printer = import "printer.capnp";
11+
12+
$Proxy.include("calculator.h");
13+
$Proxy.include("init.h");
14+
$Proxy.include("printer.h");
15+
$Proxy.includeTypes("calculator.capnp.proxy-types.h");
16+
$Proxy.includeTypes("printer.capnp.proxy-types.h");
17+
18+
interface InitInterface $Proxy.wrap("Init") {
19+
construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
20+
makeCalculator @1 (context :Proxy.Context, print :Printer.PrinterInterface) -> (result :Calculator.CalculatorInterface);
21+
makePrinter @2 (context :Proxy.Context) -> (result :Printer.PrinterInterface);
22+
}

example/init.h

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef EXAMPLE_INIT_H
6+
#define EXAMPLE_INIT_H
7+
8+
#include <calculator.h>
9+
#include <memory>
10+
#include <printer.h>
11+
12+
class Init
13+
{
14+
public:
15+
virtual ~Init() = default;
16+
virtual std::unique_ptr<Printer> makePrinter() { return nullptr; }
17+
virtual std::unique_ptr<Calculator> makeCalculator(std::unique_ptr<Printer> printer) { return nullptr; }
18+
};
19+
20+
#endif // EXAMPLE_INIT_H

example/printer.capnp

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright (c) 2021 The Bitcoin Core developers
2+
# Distributed under the MIT software license, see the accompanying
3+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
@0x893db95f456ed0e3;
6+
7+
using Cxx = import "/capnp/c++.capnp";
8+
using Proxy = import "/mp/proxy.capnp";
9+
10+
$Proxy.include("printer.h");
11+
12+
interface PrinterInterface $Proxy.wrap("Printer") {
13+
destroy @0 (context :Proxy.Context) -> ();
14+
print @1 (context :Proxy.Context, text: Text) -> ();
15+
}

0 commit comments

Comments
 (0)