๐ง Problem: Implement a simple Promise/Future mechanism in C++, mimicking then()style chaining.
โ
How We'll Solve It (in 4โ5 points):
- A Promise is an object that sets a value which will be available in the future.
- A Future is an object that retrieves the value once it's available โ possibly blocking if the value isnโt ready yet.
- We'll support
.then() chaining, which allows attaching callbacks that run when the value is set.
- We'll first implement a single-threaded version (synchronous), and then add a multithreaded version with
std::mutex and std::condition_variable to handle concurrency.
- The multithreaded version ensures that callbacks run after the value is fulfilled, even across threads.
๐น Version 1: Simple Promise/Future (No Threads)
#include <iostream>
#include <functional>
#include <vector>
template <typename T>
class SimplePromise {
T value;
bool isSet = false;
std::vector<std::function<void(T)>> callbacks;
public:
void set_value(T val) {
value = val;
isSet = true;
for (auto& cb : callbacks) {
cb(value); // Call all registered callbacks
}
}
void then(std::function<void(T)> cb) {
if (isSet) {
cb(value); // If already set, call immediately
} else {
callbacks.push_back(cb); // Otherwise, queue it
}
}
};
int main() {
SimplePromise<int> p;
p.then([](int val) {
std::cout << "Callback 1: " << val << std::endl;
});
p.set_value(42); // Triggers callback
p.then([](int val) {
std::cout << "Callback 2: " << val << std::endl;
}); // Runs immediately since value is already set
return 0;
}
๐ธ Version 2: Promise/Future with Threads (Thread-safe)
#include <iostream>
#include <functional>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <chrono>
template <typename T>
class ThreadSafePromise {
private:
T value; // The promised value
bool isSet = false; // Flag to check if value has been set
std::mutex mtx; // Mutex to protect shared state
std::condition_variable cv; // To signal waiting threads
std::vector<std::function<void(T)>> callbacks; // List of attached callbacks
public:
// Sets the value and executes any callbacks waiting
void set_value(T val) {
std::unique_lock<std::mutex> lock(mtx); // ๐ Critical Section Begins
value = val;
isSet = true;
// Run all registered callbacks in separate threads
for (auto& cb : callbacks) {
std::thread(cb, value).detach(); // โ
Detached = Fire and forget
}
// Notify any thread waiting on get()
cv.notify_all();
// ๐ Critical Section Ends when lock goes out of scope
}
// Registers a callback to be executed when the value is set
void then(std::function<void(T)> cb) {
std::unique_lock<std::mutex> lock(mtx); // ๐ Critical Section Begins
if (isSet) {
// If already set, immediately run callback in new thread
std::thread(cb, value).detach();
} else {
// Else store the callback for later execution
callbacks.push_back(cb);
}
// ๐ Critical Section Ends
}
// Blocks the caller until value is available and returns it
T get() {
std::unique_lock<std::mutex> lock(mtx); // ๐ Critical Section Begins
cv.wait(lock, [&] { return isSet; }); // Wait until isSet is true
return value;
// ๐ Critical Section Ends
}
};
int main() {
ThreadSafePromise<int> promise;
// Thread 1: Registers a callback BEFORE the value is set
std::thread t1([&]() {
promise.then([](int val) {
std::cout << "[Callback 1] Got value: " << val << std::endl;
});
});
// Thread 2: Sets the value after some delay
std::thread t2([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
promise.set_value(99); // Triggers Callback 1
});
// Thread 3: Registers a callback AFTER the value is already set
std::thread t3([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
promise.then([](int val) {
std::cout << "[Callback 2] Got value (late): " << val << std::endl;
});
});
t1.join();
t2.join();
t3.join();
return 0;
}
๐งช Sample main() to demonstrate how it works
int main() {
ThreadSafePromise<int> promise;
// Thread 1: Registers a callback BEFORE the value is set
std::thread t1([&]() {
promise.then([](int val) {
std::cout << "[Callback 1] Got value: " << val << std::endl;
});
});
// Thread 2: Sets the value after some delay
std::thread t2([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
promise.set_value(99); // Triggers Callback 1
});
// Thread 3: Registers a callback AFTER the value is already set
std::thread t3([&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
promise.then([](int val) {
std::cout << "[Callback 2] Got value (late): " << val << std::endl;
});
});
t1.join();
t2.join();
t3.join();
return 0;
}
๐ What are the Critical Sections?
| ๐ Critical Section |
Purpose |
set_value() |
Sets shared state value and isSet, and safely runs callbacks |
then() |
Checks isSet and safely appends or runs callback |
get() |
Waits on condition variable until value is available |