Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jyotindersingh/cppchan
A high-performance, thread-safe channel implementation in C++17, inspired by Go's channels
https://github.com/jyotindersingh/cppchan
Last synced: about 6 hours ago
JSON representation
A high-performance, thread-safe channel implementation in C++17, inspired by Go's channels
- Host: GitHub
- URL: https://github.com/jyotindersingh/cppchan
- Owner: JyotinderSingh
- License: mit
- Created: 2024-07-26T16:03:57.000Z (4 months ago)
- Default Branch: master
- Last Pushed: 2024-07-28T17:26:35.000Z (4 months ago)
- Last Synced: 2024-07-28T19:14:47.727Z (4 months ago)
- Language: C++
- Size: 165 KB
- Stars: 4
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# CppChan: Go-inspired Concurrency Primitive
CppChan is a high-performance, thread-safe channel in C++17, inspired by Go's channels. It provides a flexible communication mechanism for concurrent programming, supporting both buffered and unbuffered channels with synchronous and asynchronous operations.
It also supports a selector mechanism to wait on multiple channels simultaneously, allowing you to handle incoming data from multiple sources efficiently.
## Features
- Templated design for any data type
- Buffered and unbuffered channels
- Synchronous and asynchronous operations
- Blocking and non-blocking send/receive
- RAII-compliant resource handling
- Thread-safe closure mechanism
- Selector for waiting on multiple channels## Installation
To use it in your project, simply include the `channel.h` file:
```cpp
#include "channel.h"
```Ensure you're compiling with C++17 support (-std=c++17 flag for most compilers).
## Build
```bash
make
```## Test
```bash
make test
```## Thread Safety
All operations on the Channel are thread-safe. Multiple threads can safely send to and receive from the same channel concurrently.
## Error Handling
- Sending to a closed channel will throw a `std::runtime_error`.
- Receiving from a closed channel will return `std::nullopt`.## Performance Considerations
- Unbuffered channels provide stronger synchronization but may have higher overhead.
- Buffered channels can improve performance by reducing synchronization, but be mindful of the buffer size to avoid excessive memory usage.
- Use non-blocking operations (`try_send` and `try_receive`) when appropriate to avoid potential deadlocks.## API Reference
### Instantiation
```cpp
// Contructor
Channel(size_t capacity = 0)
```Creates a new channel.
- capacity: The buffer size. If 0, creates an unbuffered channel.
Use case: Create buffered or unbuffered channels for inter-thread communication.
Example
```cpp
Channel bufferedChan(5); // Buffered channel with capacity 5
Channel unbufferedChan; // Unbuffered channel
```### Sending Operations
#### Synchronous Send
```cpp
void send(const T& value)
```Sends a value to the channel. Blocks if the channel is full (buffered) or if there's no receiver (unbuffered).
Use case: Send data to other threads in a blocking manner.
Example
```cpp
ch.send(42);
```#### Asynchronous Send
```cpp
std::future async_send(const T& value)
```Asynchronously sends a value to the channel.
Use case: Send data without blocking the current thread.
Example
```cpp
auto future = ch.async_send(42);
future.wait(); // Wait for the send to complete if needed
```#### Non-blocking Send
```cpp
bool try_send(const T& value)
```Attempts to send a value to the channel without blocking.
Use case: Try to send data without blocking, useful for timeouts or polling.
Example
```cpp
if (ch.try_send(42)) {
std::cout << "Sent successfully\n";
}
```### Receiving Operations
#### Synchronous Receive
```cpp
std::optional receive()
```Receives a value from the channel. Blocks if the channel is empty.
Use case: Receive data from other threads in a blocking manner.
Example
```cpp
auto value = ch.receive();
if (value) {
std::cout << "Received: " << *value << "\n";
}
```#### Asynchronous Receive
```cpp
std::future> async_receive()
```Asynchronously receives a value from the channel.
Use case: Receive data without blocking the current thread.
Example
```cpp
auto future = ch.async_receive();
auto value = future.get(); // Wait for the receive to complete
```#### Non-blocking Receive
```cpp
std::optional try_receive()
```Attempts to receive a value from the channel without blocking.
Use case: Try to receive data without blocking, useful for timeouts or polling.
Example
```cpp
if (auto value = ch.try_receive()) {
std::cout << "Received: " << *value << "\n";
}
```### Channel Management
#### Close
```cpp
void close()
```Closes the channel. No more values can be sent after closing.
Use case: Signal that no more values will be sent on this channel.
Example
```cpp
ch.close();
```#### is_closed()
```cpp
bool is_closed() const
```Checks if the channel is closed.
Use case: Check the channel state before sending or in a loop.
Example
```cpp
while (!ch.is_closed()) {
// Perform operations
}
```#### is_empty()
```cpp
bool is_empty() const
```Checks if the channel is empty.
Use case: Check if there are any pending values to receive.
Example
```cpp
if (!ch.is_empty()) {
auto value = ch.receive();
}
```#### size()
```cpp
size_t size() const
```Returns the current number of items in the channel.
Use case: Check how many items are waiting in the channel.
Example
```cpp
std::cout << "Items in channel: " << ch.size() << "\n";
```### Selector Operations
The `Selector` class allows you to wait on multiple channels and execute callbacks when data is received.
#### Add Channel to Selector
```cpp
template
void add_receive(Channel& ch, std::function callback)
```Registers a channel and its receive callback with the selector.
Use case: Wait on multiple channels and handle incoming data.
Example
```cpp
Selector selector;
selector.add_receive(ch, [](std::string value) {
std::cout << "Received: " << value << "\n";
});
```#### Wait for Events
```cpp
void select()
```Continuously waits for events on the registered channels. This method runs in a loop (without busy-wait), processing available data until all channels are closed and emptied.
Use case: Continuously handle incoming data from multiple channels.
Example
```cpp
selector.select();
```#### Stop Selector
```cpp
void stop()
```Signals the selector to stop processing events.
Use case: Stop the selector from outside the select loop.
Example
```cpp
selector.stop();
```## Usage Examples
### Basic Usage
```cpp
#include "channel.h"
#includeint main() {
Channel ch(2); // Buffered channel with capacity 2ch.send(1);
ch.send(2);std::cout << "Received: " << *ch.receive() << std::endl;
std::cout << "Received: " << *ch.receive() << std::endl;return 0;
}
```### Asynchronous Operations
```cpp
#include "channel.h"
#include
#includeint main() {
Channel ch;auto future_send = ch.async_send(42);
auto future_recv = ch.async_receive();future_send.wait();
auto value = future_recv.get();if (value) {
std::cout << "Received: " << *value << std::endl;
}return 0;
}
```### Using Selector
```cpp
#include "channel.h"
#include "selector.h"
#include
#includevoid producer(Channel& ch, int id, int count) {
for (int i = 0; i < count; ++i) {
ch.send(id * 1000 + i);
}
ch.close();
}void consumer(Channel& ch1, Channel& ch2, Selector& selector) {
selector.add_receive(ch1, [&](int value) {
std::cout << "Channel 1 received: " << value << std::endl;
});selector.add_receive(ch2, [&](int value) {
std::cout << "Channel 2 received: " << value << std::endl;
});selector.select();
}
int main() {
Channel ch1(5);
Channel ch2(5);
Selector selector;std::thread t1(producer, std::ref(ch1), 1, 10);
std::thread t2(producer, std::ref(ch2), 2, 10);
std::thread cons(consumer, std::ref(ch1), std::ref(ch2), std::ref(selector));t1.join();
t2.join();selector.stop();
cons.join();
return 0;
}
```