Skip to content

Commit 5ffb1c6

Browse files
committed
Add GitBindingsAsync which runs the code in an isolate
Otherwise this code is blocking.
1 parent ac9d861 commit 5ffb1c6

File tree

3 files changed

+266
-6
lines changed

3 files changed

+266
-6
lines changed

bin/git.dart

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// ignore_for_file: avoid_print
22

3-
import 'dart:ffi';
43
import 'dart:io';
5-
64
import 'package:go_git_dart/go_git_dart.dart';
75

86
String _getCorrectLibrary() {
@@ -16,8 +14,7 @@ void main(List<String> arguments) {
1614
}
1715

1816
final command = arguments[0];
19-
final dylib = DynamicLibrary.open(_getCorrectLibrary());
20-
final bindings = GitBindings(dylib);
17+
final bindings = GitBindings(_getCorrectLibrary());
2118

2219
switch (command) {
2320
case 'clone':

lib/go_git_dart.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ const String _libName = 'go_git_dart';
1313
class GitBindings {
1414
late final GoGitDartBindings lib;
1515

16-
GitBindings([DynamicLibrary? dylib]) {
17-
dylib ??= () {
16+
GitBindings([String? libPath]) {
17+
var dylib = () {
18+
if (libPath != null) {
19+
return DynamicLibrary.open(libPath);
20+
}
1821
if (Platform.isMacOS || Platform.isIOS) {
1922
return DynamicLibrary.open('$_libName.framework/$_libName');
2023
}

lib/go_git_dart_async.dart

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
import 'dart:async';
2+
import 'dart:isolate';
3+
import 'dart:typed_data';
4+
5+
import 'package:go_git_dart/go_git_dart.dart';
6+
7+
class GitBindingsAsync {
8+
final String? _libPath;
9+
10+
GitBindingsAsync([this._libPath]);
11+
12+
Future<void> clone(
13+
String url,
14+
String directory,
15+
Uint8List pemBytes,
16+
String password,
17+
) async {
18+
var helperIsolateSendPort = await _helperIsolateSendPort;
19+
var requestId = _nextCloneRequestId++;
20+
var request =
21+
_CloneRequest(requestId, _libPath, url, directory, pemBytes, password);
22+
var completer = Completer<Exception?>();
23+
_cloneRequests[requestId] = completer;
24+
helperIsolateSendPort.send(request);
25+
var ex = await completer.future;
26+
if (ex != null) throw Exception(ex);
27+
}
28+
29+
Future<void> fetch(
30+
String remote,
31+
String directory,
32+
Uint8List pemBytes,
33+
String password,
34+
) async {
35+
var helperIsolateSendPort = await _helperIsolateSendPort;
36+
var requestId = _nextFetchRequestId++;
37+
var request = _FetchRequest(
38+
requestId, _libPath, remote, directory, pemBytes, password);
39+
var completer = Completer<Exception?>();
40+
_fetchRequests[requestId] = completer;
41+
helperIsolateSendPort.send(request);
42+
var ex = await completer.future;
43+
if (ex != null) throw Exception(ex);
44+
}
45+
46+
Future<void> push(
47+
String remote,
48+
String directory,
49+
Uint8List pemBytes,
50+
String password,
51+
) async {
52+
var helperIsolateSendPort = await _helperIsolateSendPort;
53+
var requestId = _nextPushRequestId++;
54+
var request = _PushRequest(
55+
requestId, _libPath, remote, directory, pemBytes, password);
56+
var completer = Completer<Exception?>();
57+
_pushRequests[requestId] = completer;
58+
helperIsolateSendPort.send(request);
59+
var ex = await completer.future;
60+
if (ex != null) throw Exception(ex);
61+
}
62+
63+
Future<String> defaultBranch(
64+
String remoteUrl,
65+
Uint8List pemBytes,
66+
String password,
67+
) async {
68+
var helperIsolateSendPort = await _helperIsolateSendPort;
69+
var requestId = _nextDefaultBranchRequestsId++;
70+
var request = _DefaultBranchRequest(
71+
requestId, _libPath, remoteUrl, pemBytes, password);
72+
var completer = Completer<(String?, Exception?)>();
73+
_defaultBranchRequests[requestId] = completer;
74+
helperIsolateSendPort.send(request);
75+
var result = await completer.future;
76+
if (result.$2 != null) throw Exception(result.$2!);
77+
return result.$1!;
78+
}
79+
}
80+
81+
class _CloneRequest {
82+
final int id;
83+
final String? libPath;
84+
final String url;
85+
final String directory;
86+
final Uint8List pemBytes;
87+
final String password;
88+
89+
const _CloneRequest(this.id, this.libPath, this.url, this.directory,
90+
this.pemBytes, this.password);
91+
}
92+
93+
class _CloneResponse {
94+
final int id;
95+
final Exception? exception;
96+
97+
const _CloneResponse(this.id, this.exception);
98+
}
99+
100+
class _FetchRequest {
101+
final int id;
102+
final String? libPath;
103+
final String remote;
104+
final String directory;
105+
final Uint8List pemBytes;
106+
final String password;
107+
108+
const _FetchRequest(this.id, this.libPath, this.remote, this.directory,
109+
this.pemBytes, this.password);
110+
}
111+
112+
class _FetchResponse {
113+
final int id;
114+
final Exception? exception;
115+
116+
const _FetchResponse(this.id, this.exception);
117+
}
118+
119+
class _PushRequest {
120+
final int id;
121+
final String? libPath;
122+
final String remote;
123+
final String directory;
124+
final Uint8List pemBytes;
125+
final String password;
126+
127+
const _PushRequest(this.id, this.libPath, this.remote, this.directory,
128+
this.pemBytes, this.password);
129+
}
130+
131+
class _PushResponse {
132+
final int id;
133+
final Exception? exception;
134+
135+
const _PushResponse(this.id, this.exception);
136+
}
137+
138+
class _DefaultBranchRequest {
139+
final int id;
140+
final String? libPath;
141+
final String remoteUrl;
142+
final Uint8List pemBytes;
143+
final String password;
144+
145+
const _DefaultBranchRequest(
146+
this.id, this.libPath, this.remoteUrl, this.pemBytes, this.password);
147+
}
148+
149+
class _DefaultBranchResponse {
150+
final int id;
151+
final String? branch;
152+
final Exception? exception;
153+
154+
const _DefaultBranchResponse(this.id, this.branch, this.exception);
155+
}
156+
157+
int _nextCloneRequestId = 0;
158+
final _cloneRequests = <int, Completer<Exception?>>{};
159+
160+
int _nextFetchRequestId = 0;
161+
final _fetchRequests = <int, Completer<Exception?>>{};
162+
163+
int _nextPushRequestId = 0;
164+
final _pushRequests = <int, Completer<Exception?>>{};
165+
166+
int _nextDefaultBranchRequestsId = 0;
167+
final _defaultBranchRequests = <int, Completer<(String?, Exception?)>>{};
168+
169+
Future<SendPort> _helperIsolateSendPort = () async {
170+
final Completer<SendPort> completer = Completer<SendPort>();
171+
172+
final ReceivePort receivePort = ReceivePort()
173+
..listen((dynamic data) {
174+
if (data is SendPort) {
175+
completer.complete(data);
176+
return;
177+
}
178+
if (data is _CloneResponse) {
179+
final completer = _cloneRequests[data.id]!;
180+
_cloneRequests.remove(data.id);
181+
completer.complete(data.exception);
182+
return;
183+
}
184+
if (data is _FetchResponse) {
185+
final completer = _fetchRequests[data.id]!;
186+
_fetchRequests.remove(data.id);
187+
completer.complete(data.exception);
188+
return;
189+
}
190+
if (data is _PushResponse) {
191+
final completer = _pushRequests[data.id]!;
192+
_pushRequests.remove(data.id);
193+
completer.complete(data.exception);
194+
return;
195+
}
196+
if (data is _DefaultBranchResponse) {
197+
final completer = _defaultBranchRequests[data.id]!;
198+
_defaultBranchRequests.remove(data.id);
199+
completer.complete((data.branch, data.exception));
200+
return;
201+
}
202+
throw UnsupportedError('Unsupported message type: ${data.runtimeType}');
203+
});
204+
205+
// Start the helper isolate.
206+
await Isolate.spawn((SendPort sendPort) async {
207+
final ReceivePort helperReceivePort = ReceivePort()
208+
..listen((dynamic data) {
209+
if (data is _CloneRequest) {
210+
try {
211+
var repo = GitBindings(data.libPath);
212+
repo.clone(data.url, data.directory, data.pemBytes, data.password);
213+
sendPort.send(_CloneResponse(data.id, null));
214+
} on Exception catch (e) {
215+
sendPort.send(_CloneResponse(data.id, e));
216+
}
217+
return;
218+
}
219+
if (data is _FetchRequest) {
220+
try {
221+
var repo = GitBindings(data.libPath);
222+
repo.fetch(
223+
data.remote, data.directory, data.pemBytes, data.password);
224+
sendPort.send(_FetchResponse(data.id, null));
225+
} on Exception catch (e) {
226+
sendPort.send(_FetchResponse(data.id, e));
227+
}
228+
return;
229+
}
230+
if (data is _PushRequest) {
231+
try {
232+
var repo = GitBindings(data.libPath);
233+
repo.fetch(
234+
data.remote, data.directory, data.pemBytes, data.password);
235+
sendPort.send(_PushResponse(data.id, null));
236+
} on Exception catch (e) {
237+
sendPort.send(_PushResponse(data.id, e));
238+
}
239+
return;
240+
}
241+
if (data is _DefaultBranchRequest) {
242+
try {
243+
var repo = GitBindings(data.libPath);
244+
var branch = repo.defaultBranch(
245+
data.remoteUrl, data.pemBytes, data.password);
246+
sendPort.send(_DefaultBranchResponse(data.id, branch, null));
247+
} on Exception catch (e) {
248+
sendPort.send(_DefaultBranchResponse(data.id, null, e));
249+
}
250+
return;
251+
}
252+
throw UnsupportedError('Unsupported message type: ${data.runtimeType}');
253+
});
254+
255+
// Send the port to the main isolate on which we can receive requests.
256+
sendPort.send(helperReceivePort.sendPort);
257+
}, receivePort.sendPort);
258+
259+
return completer.future;
260+
}();

0 commit comments

Comments
 (0)