Skip to content

Commit a3f645b

Browse files
author
pioner921227
committed
added task manager for ios
1 parent fab8a93 commit a3f645b

25 files changed

+63347
-104
lines changed

SyncTasks.podspec

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ Pod::Spec.new do |s|
1515

1616
s.source_files = "ios/**/*.{h,m,mm}", "cpp/**/*.{hpp,cpp,c,h}"
1717

18+
s.dependency 'curl'
19+
20+
1821
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
1922
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
2023
if respond_to?(:install_modules_dependencies, true)

cpp/JSManager.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// JSManager2.cpp
3+
// SyncTasks
4+
//
5+
// Created by Oleksandr Shumihin on 12/4/25.
6+
//
7+
8+
#include "JSManager.hpp"
9+
#include "TaskScheduler.h"
10+
#include "constants.h"
11+
#include "helpers/helpers.h"
12+
13+
jsi::Object createJSTaskManager(jsi::Runtime& rt) {
14+
jsi::Object taskManagerJS = jsi::Object(rt);
15+
16+
auto t_manager = std::make_shared<TaskScheduler>(THREADS_COUNT);
17+
18+
jsi::Function addTask = jsi::Function::createFromHostFunction(
19+
rt, jsi::PropNameID::forAscii(rt, ADD_TASK_KEY), 1,
20+
[t_manager](jsi::Runtime& rt, const jsi::Value& thisVal,
21+
const jsi::Value* args, size_t count) {
22+
std::shared_ptr<Task> task =
23+
args[0].asObject(rt).getNativeState<Task>(rt);
24+
25+
t_manager->addTask(std::move(task));
26+
27+
return jsi::Value(true);
28+
});
29+
30+
jsi::Function addTasks = jsi::Function::createFromHostFunction(
31+
rt, jsi::PropNameID::forAscii(rt, ADD_TASKS_KEY), 1,
32+
[t_manager](jsi::Runtime& rt, const jsi::Value& thisVal,
33+
const jsi::Value* args, size_t count) {
34+
if (!checkJSType<jsi::Array>(rt, args[0])) {
35+
throw jsi::JSError(
36+
rt, "[SyncTasksManager]: addTasks -> Argument must be an array");
37+
}
38+
39+
jsi::Array jsTasks = args[0].asObject(rt).asArray(rt);
40+
41+
for (int i = 0; i < jsTasks.length(rt); ++i) {
42+
std::shared_ptr<Task> task =
43+
jsTasks.getValueAtIndex(rt, i).asObject(rt).getNativeState<Task>(
44+
rt);
45+
46+
t_manager->addTask(std::move(task));
47+
}
48+
49+
return jsi::Value(true);
50+
});
51+
52+
jsi::Function startAll = jsi::Function::createFromHostFunction(
53+
rt, jsi::PropNameID::forAscii(rt, START_ALL_KEY), 1,
54+
[t_manager](jsi::Runtime& rt, const jsi::Value& thisVal,
55+
const jsi::Value* args, size_t count) {
56+
t_manager->start();
57+
return jsi::Value(true);
58+
});
59+
60+
jsi::Function stopAll = jsi::Function::createFromHostFunction(
61+
rt, jsi::PropNameID::forAscii(rt, STOP_ALL_KEY), 1,
62+
[t_manager](jsi::Runtime& rt, const jsi::Value& thisVal,
63+
const jsi::Value* args, size_t count) {
64+
t_manager->stop();
65+
return jsi::Value(true);
66+
});
67+
68+
taskManagerJS.setNativeState(rt, t_manager);
69+
70+
taskManagerJS.setProperty(rt, ADD_TASK_KEY, std::move(addTask));
71+
taskManagerJS.setProperty(rt, ADD_TASKS_KEY, std::move(addTasks));
72+
taskManagerJS.setProperty(rt, START_ALL_KEY, std::move(startAll));
73+
taskManagerJS.setProperty(rt, STOP_ALL_KEY, std::move(stopAll));
74+
75+
taskManagerJS.setExternalMemoryPressure(rt, sizeof(TaskScheduler));
76+
77+
return taskManagerJS;
78+
}

cpp/JSManager.hpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//
2+
// JSManager2.hpp
3+
// SyncTasks
4+
//
5+
// Created by Oleksandr Shumihin on 12/4/25.
6+
//
7+
8+
#ifndef JSManager_hpp
9+
#define JSManager_hpp
10+
11+
#include <stdio.h>
12+
13+
14+
#include "jsi/jsi.h"
15+
16+
using namespace facebook;
17+
18+
19+
jsi::Object createJSTaskManager(jsi::Runtime& rt);
20+
21+
22+
23+
24+
25+
#endif /* JSManager_hpp */

cpp/JSTask.cpp

Lines changed: 173 additions & 0 deletions
Large diffs are not rendered by default.

cpp/JSTask.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// JSTask2.hpp
3+
// SyncTasks
4+
//
5+
// Created by Oleksandr Shumihin on 12/4/25.
6+
//
7+
8+
#ifndef JSTask_hpp
9+
#define JSTask_hpp
10+
11+
#include <stdio.h>
12+
13+
#include "jsi/jsi.h"
14+
#include <ReactCommon/CallInvoker.h>
15+
16+
using namespace facebook;
17+
18+
jsi::Function createJSTaskCreator(jsi::Runtime& rt,std::shared_ptr<react::CallInvoker> callInvoker);
19+
20+
#endif /* JSTask_hpp */

cpp/TaskScheduler.h

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
//
2+
// TaskScheduler.cpp
3+
// intch_application
4+
//
5+
// Created by Oleksandr Shumihin on 10/4/25.
6+
// Copyright © 2025 Facebook. All rights reserved.
7+
//
8+
9+
#pragma once
10+
11+
#include "atomic"
12+
#include "jsi/jsi.h"
13+
#include "queue"
14+
#include "thread"
15+
16+
using namespace std;
17+
using namespace facebook;
18+
19+
class Task : public jsi::NativeState {
20+
public:
21+
Task(std::string id, int intervalMs, std::function<void(Task&)> job)
22+
: id_(std::move(id)), intervalMs_(intervalMs), job_(std::move(job)) {}
23+
24+
~Task() = default;
25+
26+
void run() {
27+
if (job_ && ready_ && !stopped_)
28+
job_(*this);
29+
}
30+
31+
const std::string& getId() const { return id_; }
32+
int getInterval() const { return intervalMs_; }
33+
34+
void stop() { stopped_ = true; }
35+
36+
void start() {
37+
if (ready_) {
38+
stopped_ = false;
39+
}
40+
}
41+
42+
bool isStopped() const { return stopped_; }
43+
bool isReady() const { return ready_; }
44+
45+
void makeReady() { ready_ = true; }
46+
47+
void setLastBodyHash(size_t value){
48+
lastBodyHash_ = value;
49+
}
50+
51+
bool hasSameBodyHash(size_t value) const {
52+
return lastBodyHash_ == value;
53+
}
54+
55+
private:
56+
std::string id_;
57+
std::function<void(Task& t)> job_;
58+
std::atomic<bool> stopped_ = true;
59+
size_t lastBodyHash_;
60+
int intervalMs_;
61+
bool ready_ = false;
62+
};
63+
64+
// ========== ThreadPool ==========
65+
class ThreadPool {
66+
public:
67+
ThreadPool(size_t numThreads) {
68+
stop_ = false;
69+
for (size_t i = 0; i < numThreads; ++i) {
70+
workers_.emplace_back([this]() {
71+
while (true) {
72+
std::function<void()> task;
73+
74+
{
75+
std::unique_lock lock(queueMutex_);
76+
cv_.wait(lock, [this]() { return stop_ || !tasks_.empty(); });
77+
78+
if (stop_ && tasks_.empty())
79+
return;
80+
81+
task = std::move(tasks_.front());
82+
tasks_.pop();
83+
}
84+
85+
task();
86+
}
87+
});
88+
}
89+
}
90+
91+
~ThreadPool() {
92+
{
93+
std::unique_lock lock(queueMutex_);
94+
stop_ = true;
95+
}
96+
cv_.notify_all();
97+
for (auto& worker : workers_) {
98+
if (worker.joinable())
99+
worker.join();
100+
}
101+
}
102+
103+
void enqueue(std::function<void()> job) {
104+
{
105+
std::unique_lock lock(queueMutex_);
106+
tasks_.push(std::move(job));
107+
}
108+
cv_.notify_one();
109+
}
110+
111+
private:
112+
std::vector<std::thread> workers_;
113+
std::queue<std::function<void()>> tasks_;
114+
std::mutex queueMutex_;
115+
std::condition_variable cv_;
116+
bool stop_;
117+
};
118+
119+
// ========== TaskEntry (for priority_queue) ==========
120+
struct TaskEntry {
121+
std::chrono::steady_clock::time_point nextRun;
122+
std::shared_ptr<Task> task;
123+
124+
TaskEntry(std::chrono::steady_clock::time_point next,std::shared_ptr<Task> t):nextRun(std::move(next)), task(std::move(t)) {
125+
task->makeReady();
126+
}
127+
128+
bool operator<(const TaskEntry& other) const {
129+
return nextRun > other.nextRun; // min-heap
130+
}
131+
};
132+
133+
// ========== TaskScheduler ==========
134+
class TaskScheduler : public jsi::NativeState {
135+
public:
136+
TaskScheduler(int numThreads = 4) : pool_(numThreads), running_(false) {}
137+
~TaskScheduler() { stop(); };
138+
139+
void addTask(const std::shared_ptr<Task>& task) {
140+
std::unique_lock lock(mutex_);
141+
142+
if(taskMap_.contains(task->getId())){
143+
return;
144+
}
145+
146+
tasks_.emplace(std::chrono::steady_clock::now() +
147+
std::chrono::milliseconds(task->getInterval()),
148+
task);
149+
150+
taskMap_[task->getId()] = task;
151+
cv_.notify_all();
152+
}
153+
154+
void start() {
155+
if (!running_) {
156+
running_ = true;
157+
schedulerThread_ = std::thread(&TaskScheduler::schedulerLoop, this);
158+
}
159+
160+
for (auto& [id, task] : taskMap_) {
161+
task->start();
162+
}
163+
}
164+
165+
void stop() {
166+
{
167+
std::unique_lock lock(mutex_);
168+
running_ = false;
169+
}
170+
171+
for (auto& [id, task] : taskMap_) {
172+
task->stop();
173+
}
174+
cv_.notify_all();
175+
if (schedulerThread_.joinable())
176+
schedulerThread_.join();
177+
}
178+
179+
bool hasTaskWithId(const std::string& id){
180+
return taskMap_.contains(id);
181+
}
182+
183+
private:
184+
std::priority_queue<TaskEntry> tasks_;
185+
std::unordered_map<std::string, std::shared_ptr<Task>> taskMap_;
186+
std::mutex mutex_;
187+
std::condition_variable cv_;
188+
ThreadPool pool_;
189+
std::thread schedulerThread_;
190+
bool running_;
191+
192+
void schedulerLoop() {
193+
while (running_) {
194+
std::unique_lock lock(mutex_);
195+
196+
if (tasks_.empty()) {
197+
cv_.wait(lock);
198+
continue;
199+
}
200+
201+
auto now = std::chrono::steady_clock::now();
202+
TaskEntry entry = tasks_.top();
203+
204+
if (entry.nextRun <= now) {
205+
tasks_.pop();
206+
207+
auto taskToRun = entry.task;
208+
pool_.enqueue([taskToRun]() { taskToRun->run(); });
209+
210+
// Reschedule
211+
entry.nextRun =
212+
now + std::chrono::milliseconds(taskToRun->getInterval());
213+
tasks_.emplace(entry);
214+
} else {
215+
cv_.wait_until(lock, entry.nextRun);
216+
}
217+
}
218+
}
219+
};

cpp/constants.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// constants.hpp
3+
// Pods
4+
//
5+
// Created by Oleksandr Shumihin on 12/4/25.
6+
//
7+
8+
#pragma once
9+
10+
constexpr const char* CONFIG_KEY = "config";
11+
constexpr const char* URL_KEY = "url";
12+
constexpr const char* INTERVAL_KEY = "interval";
13+
constexpr const char* HEADERS_KEY = "headers";
14+
constexpr const char* ON_DATA_KEY = "onData";
15+
constexpr const char* ON_ERROR_KEY = "onError";
16+
constexpr const char* SYNC_TASK_MANAGER_KEY = "SyncTasksManager";
17+
constexpr const char* CREATE_TASK_KEY = "createTask";
18+
19+
constexpr const char* ADD_TASK_KEY = "addTask";
20+
constexpr const char* ADD_TASKS_KEY = "addTasks";
21+
constexpr const char* START_ALL_KEY = "startAll";
22+
constexpr const char* STOP_ALL_KEY = "stopAll";
23+
24+
constexpr int THREADS_COUNT = 4;

0 commit comments

Comments
 (0)