Skip to content

Commit 17a28b8

Browse files
committed
Remove shared memory from README; Add static_assert to make sure T is default constructible
1 parent f51a909 commit 17a28b8

File tree

2 files changed

+51
-162
lines changed

2 files changed

+51
-162
lines changed

README.md

Lines changed: 47 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)](#platform-support)
66
[![Header-only](https://img.shields.io/badge/header--only-yes-brightgreen.svg)](#installation)
77
[![Lock-free](https://img.shields.io/badge/concurrency-lock--free-orange.svg)](#architecture)
8+
[![CI](https://github.com/SlickQuant/slick_object_pool/actions/workflows/ci.yml/badge.svg)](https://github.com/SlickQuant/slick_object_pool/actions/workflows/ci.yml)
9+
[![GitHub release](https://img.shields.io/github/v/release/SlickQuant/slick_object_pool)](https://github.com/SlickQuant/slick_object_pool/releases)
810

911
A high-performance, lock-free object pool for C++20 with multi-threading support. Designed for real-time systems, game engines, high-frequency trading, and any application requiring predictable, low-latency object allocation.
1012

@@ -15,15 +17,13 @@ A high-performance, lock-free object pool for C++20 with multi-threading support
1517
- [Features](#features)
1618
- [🚀 Performance](#-performance)
1719
- [🔧 Architecture](#-architecture)
18-
- [🌐 Shared Memory](#-shared-memory)
1920
- [⚡ Use Cases](#-use-cases)
2021
- [Quick Start](#quick-start)
2122
- [Installation](#installation)
2223
- [Header-Only Integration](#header-only-integration)
2324
- [CMake Integration](#cmake-integration)
2425
- [Usage Examples](#usage-examples)
2526
- [Basic Usage](#basic-usage)
26-
- [Shared Memory (Inter-Process)](#shared-memory-inter-process)
2727
- [Multi-Threaded Usage](#multi-threaded-usage)
2828
- [Architecture](#architecture)
2929
- [Lock-Free MPMC Design](#lock-free-mpmc-design)
@@ -49,8 +49,6 @@ A high-performance, lock-free object pool for C++20 with multi-threading support
4949
- [Best Practices](#best-practices)
5050
- [Pool Size Selection](#pool-size-selection)
5151
- [Pool Exhaustion Handling](#pool-exhaustion-handling)
52-
- [Shared Memory Lifecycle](#shared-memory-lifecycle)
53-
- [Type Design for Shared Memory](#type-design-for-shared-memory)
5452
- [Limitations](#limitations)
5553
- [FAQ](#faq)
5654
- [Contributing](#contributing)
@@ -74,18 +72,11 @@ A high-performance, lock-free object pool for C++20 with multi-threading support
7472
- **Type-safe** - Static assertions ensure compatible types
7573
- **Cross-platform** - Windows, Linux, macOS, and Unix-like systems
7674

77-
### 🌐 Shared Memory
78-
- **Inter-process communication** - Share pools across multiple processes
79-
- **Zero-copy** - Direct memory access without serialization
80-
- **Automatic synchronization** - Lock-free coordination between processes
81-
- **Lifecycle management** - Automatic cleanup on process termination
82-
8375
### ⚡ Use Cases
8476
- Real-time systems (robotics, industrial control)
8577
- Game engines (entity management, particle systems)
8678
- High-frequency trading systems
8779
- Network servers (connection pooling, buffer management)
88-
- Multi-process data pipelines
8980
- Any scenario requiring predictable allocation performance
9081

9182
## Quick Start
@@ -192,63 +183,6 @@ int main() {
192183
}
193184
```
194185
195-
### Shared Memory (Inter-Process)
196-
197-
**Process 1: Create and populate shared pool**
198-
199-
```cpp
200-
#include <slick/object_pool.h>
201-
202-
struct SharedData {
203-
int64_t timestamp;
204-
double price;
205-
};
206-
207-
int main() {
208-
// Create shared memory pool (512 objects, power of 2)
209-
slick::ObjectPool<SharedData> pool(512, "market_data_pool");
210-
211-
// Pool is automatically initialized and ready to use
212-
for (int i = 0; i < 100; ++i) {
213-
SharedData* data = pool.allocate_object();
214-
data->timestamp = get_current_time();
215-
data->price = get_market_price();
216-
217-
// Process data...
218-
219-
pool.free_object(data);
220-
}
221-
222-
return 0;
223-
}
224-
```
225-
226-
**Process 2: Attach to existing shared pool**
227-
228-
```cpp
229-
#include <slick/object_pool.h>
230-
231-
struct SharedData {
232-
int64_t timestamp;
233-
double price;
234-
};
235-
236-
int main() {
237-
// Attach to existing shared pool
238-
slick::ObjectPool<SharedData> pool("market_data_pool");
239-
240-
// Use the shared pool
241-
SharedData* data = pool.allocate_object();
242-
243-
// Process shared data...
244-
std::cout << "Price: " << data->price << std::endl;
245-
246-
pool.free_object(data);
247-
248-
return 0;
249-
}
250-
```
251-
252186
### Multi-Threaded Usage
253187
254188
```cpp
@@ -276,7 +210,7 @@ void worker_thread(slick::ObjectPool<WorkItem>& pool, int thread_id) {
276210
}
277211
278212
int main() {
279-
// Create shared pool (must be power of 2)
213+
// Create pool (must be power of 2)
280214
slick::ObjectPool<WorkItem> pool(2048);
281215
282216
// Launch multiple producer/consumer threads
@@ -330,23 +264,12 @@ Cache Lines 2+ - Shared data:
330264

331265
### Memory Layout
332266

333-
**Local Memory Mode:**
334267
```
335268
ObjectPool instance
336269
├─ Heap: buffer_[size_] (actual objects)
337270
├─ Heap: control_[size_] (slot metadata)
338271
├─ Heap: free_objects_[size_] (free list)
339-
└─ Stack: reserved_, consumed_ (local atomics)
340-
```
341-
342-
**Shared Memory Mode:**
343-
```
344-
Shared Memory Segment "pool_name"
345-
├─ [0-63]: reserved_ + size_ (Producer cache line)
346-
├─ [64-127]: consumed_ (Consumer cache line)
347-
├─ [128+]: control_[size_] (Slot metadata)
348-
├─ [N+]: buffer_[size_] (Object storage)
349-
└─ [M+]: free_objects_[size_] (Free list)
272+
└─ Stack: reserved_, consumed_ (atomics)
350273
```
351274

352275
## Performance
@@ -365,13 +288,13 @@ Tested on: Intel Xeon E5-2680 v4 @ 2.4GHz, 256GB RAM, Linux 5.15
365288

366289
### Comparison with Alternatives
367290

368-
| Implementation | Allocation Latency | Thread Safety | Shared Memory |
369-
|----------------|-------------------|---------------|---------------|
370-
| slick_object_pool | ~12-35 ns | Lock-free MPMC | ✅ Yes |
371-
| std::allocator | ~50-200 ns | Thread-local | ❌ No |
372-
| boost::pool | ~20-40 ns | Mutex-based | ❌ No |
373-
| tcmalloc | ~30-60 ns | Thread-local | ❌ No |
374-
| jemalloc | ~25-50 ns | Thread-local | ❌ No |
291+
| Implementation | Allocation Latency | Thread Safety |
292+
|----------------|-------------------|---------------|
293+
| slick_object_pool | ~12-35 ns | Lock-free |
294+
| std::allocator | ~50-200 ns | Thread-local |
295+
| boost::pool | ~20-40 ns | Mutex-based |
296+
| tcmalloc | ~30-60 ns | Thread-local |
297+
| jemalloc | ~25-50 ns | Thread-local |
375298

376299
*Note: Benchmarks are system-dependent. Run your own tests for production use.*
377300

@@ -381,19 +304,11 @@ Tested on: Intel Xeon E5-2680 v4 @ 2.4GHz, 256GB RAM, Linux 5.15
381304

382305
```cpp
383306
// Create pool in local memory
384-
ObjectPool(uint32_t size, const char* shm_name = nullptr);
385-
386-
// Open existing shared memory pool
387-
ObjectPool(const char* shm_name);
307+
ObjectPool(uint32_t size);
388308
```
389309
390310
**Parameters:**
391311
- `size`: Number of objects in pool (must be power of 2)
392-
- `shm_name`: Name for shared memory segment (nullptr for local memory)
393-
394-
**Throws:**
395-
- `std::runtime_error`: If shared memory allocation fails
396-
- `std::invalid_argument`: If size is not power of 2 (assertion in debug)
397312
398313
### Methods
399314
@@ -410,9 +325,7 @@ void free_object(T* obj);
410325
Returns an object to the pool if it belongs to the pool, otherwise deletes it.
411326
412327
```cpp
413-
// Query methods
414-
bool own_buffer() const noexcept; // Is this the pool owner?
415-
bool use_shm() const noexcept; // Using shared memory?
328+
// Query method
416329
constexpr uint32_t size() const noexcept; // Pool size
417330
```
418331

@@ -421,31 +334,29 @@ constexpr uint32_t size() const noexcept; // Pool size
421334
Objects stored in the pool must satisfy:
422335

423336
```cpp
424-
static_assert(std::is_trivially_copyable_v<T>);
425-
static_assert(std::is_standard_layout_v<T>);
337+
static_assert(std::is_default_constructible_v<T>);
426338
```
427339

428340
**Valid types:**
429341
- POD types (int, float, etc.)
430-
- Structs with trivial copy/move
431-
- Arrays of valid types
342+
- std::string, std::vector, and other standard containers
343+
- Structs with default constructors
344+
- Classes with default constructors
432345

433346
**Invalid types:**
434-
- Types with virtual functions
435-
- Types with user-defined copy constructors
436-
- Types containing pointers to process-local memory (for shared memory mode)
437-
- std::string, std::vector (contains pointers)
347+
- Types without default constructors
348+
- Types with deleted default constructors
438349

439350
## Platform Support
440351

441-
| Platform | Status | API Used |
442-
|----------|--------|----------|
443-
| Windows (MSVC) | ✅ Tested | File Mapping API |
444-
| Windows (MinGW) | ✅ Tested | File Mapping API |
445-
| Linux | ✅ Tested | POSIX shm_open/mmap |
446-
| macOS | ✅ Tested | POSIX shm_open/mmap |
447-
| FreeBSD | ⚠️ Should work | POSIX shm_open/mmap |
448-
| Unix-like | ⚠️ Should work | POSIX shm_open/mmap |
352+
| Platform | Status |
353+
|----------|--------|
354+
| Windows (MSVC) | ✅ Tested |
355+
| Windows (MinGW) | ✅ Tested |
356+
| Linux | ✅ Tested |
357+
| macOS | ✅ Tested |
358+
| FreeBSD | ⚠️ Should work |
359+
| Unix-like | ⚠️ Should work |
449360

450361
## Requirements
451362

@@ -545,7 +456,6 @@ sudo cmake --install .
545456
-**Multiple producers** can call `allocate_object()` concurrently
546457
-**Multiple consumers** can call `free_object()` concurrently
547458
-**Mixed operations** (allocate + free) are safe
548-
-**Shared memory** pools are safe across processes
549459
-**reset()** is NOT thread-safe (use when no other threads are active)
550460

551461
### Memory Ordering
@@ -584,76 +494,51 @@ T* obj = pool.allocate_object(); // May return heap-allocated object
584494
pool.free_object(obj); // Works for pool or heap objects
585495
```
586496

587-
### Shared Memory Lifecycle
588-
589-
```cpp
590-
// Process 1: Creates pool
591-
{
592-
slick::ObjectPool<T> owner(1024, "my_pool");
593-
// Pool exists in shared memory
594-
// ...
595-
} // Pool destroyed when owner exits
596-
597-
// Process 2: Must handle pool disappearing
598-
try {
599-
slick::ObjectPool<T> client("my_pool");
600-
// Use pool...
601-
} catch (const std::runtime_error& e) {
602-
// Pool doesn't exist or was deleted
603-
}
604-
```
605-
606-
### Type Design for Shared Memory
497+
### Type Design
607498

608499
```cpp
609-
// ✅ Good: POD struct
610-
struct GoodType {
500+
// ✅ Good: Simple POD struct
501+
struct SimpleType {
611502
int id;
612503
double values[10];
613504
char name[32];
614505
};
615506

616-
// ❌ Bad: Contains pointers
617-
struct BadType {
507+
// ✅ Good: Types with STL containers
508+
struct ComplexType {
618509
int id;
619-
std::string name; // Contains pointer!
620-
std::vector<double> v; // Contains pointer!
510+
std::string name; // OK!
511+
std::vector<double> v; // OK!
512+
};
513+
514+
// ❌ Bad: No default constructor
515+
struct BadType {
516+
BadType(int x) : value(x) {} // No default constructor
517+
int value;
621518
};
622519

623-
// ✅ Workaround: Fixed-size arrays
520+
// ✅ Fix: Add default constructor
624521
struct FixedType {
625-
int id;
626-
char name[32];
627-
double values[10];
628-
size_t value_count;
522+
FixedType() = default; // Default constructor
523+
FixedType(int x) : value(x) {}
524+
int value = 0;
629525
};
630526
```
631527
632528
## Limitations
633529
634530
1. **Pool size must be power of 2** - Required for efficient bitwise indexing
635-
2. **Type must be trivially copyable** - Required for shared memory safety
531+
2. **Type must be default constructible** - Required for pool initialization
636532
3. **No automatic resize** - Pool size is fixed at construction
637-
4. **Shared memory persistence** - Pool persists until owner process exits or calls destructor
638-
5. **No memory reclamation** - Objects returned to pool are reused, not freed
639-
6. **Platform-specific shared memory** - Windows and POSIX implementations differ
533+
4. **No memory reclamation** - Objects returned to pool are reused, not freed
640534
641535
## FAQ
642536
643537
**Q: What happens when the pool is exhausted?**
644538
A: `allocate_object()` automatically allocates from heap. `free_object()` detects and deletes heap-allocated objects.
645539
646540
**Q: Can I use std::string or std::vector in pooled objects?**
647-
A: No, for shared memory mode. These types contain pointers to process-local memory. Use fixed-size arrays instead.
648-
649-
**Q: How do I know if I'm using shared memory or local memory?**
650-
A: Call `pool.use_shm()` to check.
651-
652-
**Q: Can multiple processes create the same shared pool?**
653-
A: Yes, the first process becomes the owner. Others attach to the existing pool.
654-
655-
**Q: What happens if the owner process crashes?**
656-
A: On POSIX: Pool persists until explicitly unlinked. On Windows: Pool is automatically cleaned up.
541+
A: Yes! The pool works with any default constructible type, including std::string, std::vector, and other standard containers.
657542
658543
**Q: Is the pool real-time safe?**
659544
A: Operations are lock-free but not wait-free. Allocation may fail and fall back to heap allocation.

include/slick/object_pool.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ namespace slick {
6464
*/
6565
template<typename T>
6666
class ObjectPool {
67+
// Type safety check: T must be default constructible
68+
static_assert(std::is_default_constructible_v<T>,
69+
"T must be default constructible");
70+
6771
/// Hardware cache line size (typically 64 bytes, auto-detected if available)
6872
#ifdef __cpp_lib_hardware_interference_size
6973
static constexpr size_t CACHE_LINE_SIZE = std::hardware_destructive_interference_size;

0 commit comments

Comments
 (0)