Skip to content

Commit 41123c7

Browse files
authored
Merge pull request bonsai-rx#11 from glopesdev/gil-support
Add operator to ensure notifications are emitted inside the GIL
2 parents f6d789a + 181f3d0 commit 41123c7

9 files changed

Lines changed: 137 additions & 113 deletions

File tree

src/Bonsai.Scripting.Python/CreateModule.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.ComponentModel;
3-
using System.IO;
43
using System.Reactive.Linq;
54
using Python.Runtime;
65

@@ -34,11 +33,11 @@ public class CreateModule : Source<PyModule>
3433
/// </returns>
3534
public override IObservable<PyModule> Generate()
3635
{
37-
return RuntimeManager.RuntimeSource.SelectMany(runtime =>
38-
{
39-
var module = RuntimeManager.CreateModule(Name ?? string.Empty, ScriptPath);
40-
return Observable.Return(module);
41-
});
36+
var name = Name;
37+
var scriptPath = ScriptPath;
38+
return RuntimeManager.RuntimeSource
39+
.ObserveOnGIL()
40+
.Select(_ => RuntimeManager.CreateModule(name ?? string.Empty, scriptPath));
4241
}
4342
}
4443
}

src/Bonsai.Scripting.Python/Eval.cs

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,9 @@ public class Eval : Combinator<PyObject>
4444
/// </returns>
4545
public override IObservable<PyObject> Process<TSource>(IObservable<TSource> source)
4646
{
47-
return RuntimeManager.RuntimeSource.SelectMany(runtime =>
48-
{
49-
return source.Select(_ =>
50-
{
51-
using (Py.GIL())
52-
{
53-
var module = Module ?? runtime.MainModule;
54-
return module.Eval(Expression);
55-
}
56-
});
57-
});
47+
return RuntimeManager.RuntimeSource
48+
.GetModuleOrDefaultAsync(Module)
49+
.SelectMany(module => source.Select(_ => module.Eval(Expression)));
5850
}
5951

6052
/// <summary>
@@ -69,13 +61,7 @@ public override IObservable<PyObject> Process<TSource>(IObservable<TSource> sour
6961
/// </returns>
7062
public IObservable<PyObject> Process(IObservable<PyModule> source)
7163
{
72-
return source.Select(module =>
73-
{
74-
using (Py.GIL())
75-
{
76-
return module.Eval(Expression);
77-
}
78-
});
64+
return source.Select(module => module.Eval(Expression));
7965
}
8066

8167
/// <summary>
@@ -90,13 +76,7 @@ public IObservable<PyObject> Process(IObservable<PyModule> source)
9076
/// </returns>
9177
public IObservable<PyObject> Process(IObservable<RuntimeManager> source)
9278
{
93-
return source.Select(runtime =>
94-
{
95-
using (Py.GIL())
96-
{
97-
return runtime.MainModule.Eval(Expression);
98-
}
99-
});
79+
return source.Select(runtime => runtime.MainModule.Eval(Expression));
10080
}
10181
}
10282
}

src/Bonsai.Scripting.Python/Exec.cs

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,9 @@ public class Exec : Combinator<PyModule>
4545
/// </returns>
4646
public override IObservable<PyModule> Process<TSource>(IObservable<TSource> source)
4747
{
48-
return RuntimeManager.RuntimeSource.SelectMany(runtime =>
49-
{
50-
return source.Select(_ =>
51-
{
52-
using (Py.GIL())
53-
{
54-
var module = Module ?? runtime.MainModule;
55-
return module.Exec(Script);
56-
}
57-
});
58-
});
48+
return RuntimeManager.RuntimeSource
49+
.GetModuleOrDefaultAsync(Module)
50+
.SelectMany(module => source.Select(_ => module.Exec(Script)));
5951
}
6052

6153
/// <summary>
@@ -71,13 +63,7 @@ public override IObservable<PyModule> Process<TSource>(IObservable<TSource> sour
7163
/// </returns>
7264
public IObservable<PyModule> Process(IObservable<PyModule> source)
7365
{
74-
return source.Select(module =>
75-
{
76-
using (Py.GIL())
77-
{
78-
return module.Exec(Script);
79-
}
80-
});
66+
return source.Select(module => module.Exec(Script));
8167
}
8268

8369
/// <summary>
@@ -92,13 +78,7 @@ public IObservable<PyModule> Process(IObservable<PyModule> source)
9278
/// </returns>
9379
public IObservable<PyModule> Process(IObservable<RuntimeManager> source)
9480
{
95-
return source.Select(runtime =>
96-
{
97-
using (Py.GIL())
98-
{
99-
return runtime.MainModule.Exec(Script);
100-
}
101-
});
81+
return source.Select(runtime => runtime.MainModule.Exec(Script));
10282
}
10383
}
10484
}

src/Bonsai.Scripting.Python/Get.cs

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,10 @@ public class Get : Source<PyObject>
3838
/// </returns>
3939
public override IObservable<PyObject> Generate()
4040
{
41-
return RuntimeManager.RuntimeSource.SelectMany(runtime =>
42-
{
43-
var module = Module ?? runtime.MainModule;
44-
return Observable.Return(module.Get(VariableName));
45-
});
41+
return RuntimeManager.RuntimeSource
42+
.GetModuleOrDefaultAsync(Module)
43+
.ObserveOnGIL()
44+
.Select(module => module.Get(VariableName));
4645
}
4746

4847
/// <summary>
@@ -61,17 +60,9 @@ public override IObservable<PyObject> Generate()
6160
/// </returns>
6261
public IObservable<PyObject> Generate<TSource>(IObservable<TSource> source)
6362
{
64-
return RuntimeManager.RuntimeSource.SelectMany(runtime =>
65-
{
66-
return source.Select(_ =>
67-
{
68-
using (Py.GIL())
69-
{
70-
var module = Module ?? runtime.MainModule;
71-
return module.Get(VariableName);
72-
}
73-
});
74-
});
63+
return RuntimeManager.RuntimeSource
64+
.GetModuleOrDefaultAsync(Module)
65+
.SelectMany(module => source.Select(_ => module.Get(VariableName)));
7566
}
7667

7768
/// <summary>
@@ -89,13 +80,7 @@ public IObservable<PyObject> Generate<TSource>(IObservable<TSource> source)
8980
/// </returns>
9081
public IObservable<PyObject> Process(IObservable<PyModule> source)
9182
{
92-
return source.Select(module =>
93-
{
94-
using (Py.GIL())
95-
{
96-
return module.Get(VariableName);
97-
}
98-
});
83+
return source.Select(module => module.Get(VariableName));
9984
}
10085

10186
/// <summary>
@@ -112,13 +97,7 @@ public IObservable<PyObject> Process(IObservable<PyModule> source)
11297
/// </returns>
11398
public IObservable<PyObject> Process(IObservable<RuntimeManager> source)
11499
{
115-
return source.Select(runtime =>
116-
{
117-
using (Py.GIL())
118-
{
119-
return runtime.MainModule.Get(VariableName);
120-
}
121-
});
100+
return source.Select(runtime => runtime.MainModule.Get(VariableName));
122101
}
123102
}
124103
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
using System.ComponentModel;
3+
4+
namespace Bonsai.Scripting.Python
5+
{
6+
/// <summary>
7+
/// Represents an operator that gets the Python runtime object which can be used
8+
/// to import modules, evaluate expressions, and pass data to and from Python.
9+
/// </summary>
10+
/// <remarks>
11+
/// The runtime object notification is emitted while holding the global interpreter lock.
12+
/// </remarks>
13+
[Description("Gets the Python runtime object which can be used to import modules, evaluate expressions, and pass data to and from Python.")]
14+
public class GetRuntime : Source<RuntimeManager>
15+
{
16+
/// <summary>
17+
/// Wraps an observable sequence to ensure all notifications are emitted
18+
/// while holding the Python global interpreter lock.
19+
/// </summary>
20+
/// <returns>
21+
/// An observable sequence that returns the active <see cref="RuntimeManager"/>
22+
/// object on subscription. The value is emitted while holding the global
23+
/// interpreter lock.
24+
/// </returns>
25+
public override IObservable<RuntimeManager> Generate()
26+
{
27+
return RuntimeManager.RuntimeSource.ObserveOnGIL();
28+
}
29+
}
30+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.Reactive;
3+
using System.Reactive.Linq;
4+
using Python.Runtime;
5+
6+
namespace Bonsai.Scripting.Python
7+
{
8+
static class ObservableExtensions
9+
{
10+
public static IObservable<PyModule> GetModuleOrDefaultAsync(this IObservable<RuntimeManager> source, PyModule module)
11+
{
12+
return module != null
13+
? Observable.Return(module)
14+
: source.Select(runtime => runtime.MainModule);
15+
}
16+
17+
public static IObservable<TSource> ObserveOnGIL<TSource>(this IObservable<TSource> source)
18+
{
19+
return Observable.Create<TSource>(observer =>
20+
{
21+
var sourceObserver = Observer.Create<TSource>(
22+
value =>
23+
{
24+
using (Py.GIL())
25+
{
26+
observer.OnNext(value);
27+
}
28+
},
29+
observer.OnError,
30+
observer.OnCompleted);
31+
return source.SubscribeSafe(sourceObserver);
32+
});
33+
}
34+
}
35+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
using System.ComponentModel;
3+
using System.Linq;
4+
using System.Reactive.Linq;
5+
6+
namespace Bonsai.Scripting.Python
7+
{
8+
/// <summary>
9+
/// Represents an operator that wraps the source sequence to ensure all notifications
10+
/// are emitted while holding the Python global interpreter lock.
11+
/// </summary>
12+
[Description("Wraps the source sequence to ensure all notifications are emitted while holding the Python global interpreter lock.")]
13+
public class ObserveOnGIL : Combinator
14+
{
15+
/// <summary>
16+
/// Wraps an observable sequence to ensure all notifications are emitted
17+
/// while holding the Python global interpreter lock.
18+
/// </summary>
19+
/// <typeparam name="TSource">
20+
/// The type of the elements in the <paramref name="source"/> sequence.
21+
/// </typeparam>
22+
/// <param name="source">The source sequence to wrap.</param>
23+
/// <returns>
24+
/// An observable sequence that is identical to the <paramref name="source"/>
25+
/// sequence but where there is an additional side effect of ensuring that
26+
/// all notifications are emitted inside the Python global interpreter lock.
27+
/// </returns>
28+
public override IObservable<TSource> Process<TSource>(IObservable<TSource> source)
29+
{
30+
return RuntimeManager.RuntimeSource.SelectMany(_ => source.ObserveOnGIL());
31+
}
32+
}
33+
}

src/Bonsai.Scripting.Python/RuntimeManager.cs

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ internal RuntimeManager(string pythonHome, string scriptPath, IObserver<RuntimeM
3131
using (Py.GIL())
3232
{
3333
MainModule = CreateModule(scriptPath: scriptPath);
34+
observer.OnNext(this);
3435
}
35-
observer.OnNext(this);
3636
});
3737
}
3838

@@ -79,24 +79,21 @@ static string ReadAllText(string path)
7979

8080
internal static DynamicModule CreateModule(string name = "", string scriptPath = "")
8181
{
82-
using (Py.GIL())
82+
var module = new DynamicModule(name);
83+
if (!string.IsNullOrEmpty(scriptPath))
8384
{
84-
var module = new DynamicModule(name);
85-
if (!string.IsNullOrEmpty(scriptPath))
85+
try
8686
{
87-
try
88-
{
89-
var code = ReadAllText(scriptPath);
90-
module.Exec(code);
91-
}
92-
catch (Exception)
93-
{
94-
module.Dispose();
95-
throw;
96-
}
87+
var code = ReadAllText(scriptPath);
88+
module.Exec(code);
89+
}
90+
catch (Exception)
91+
{
92+
module.Dispose();
93+
throw;
9794
}
98-
return module;
9995
}
96+
return module;
10097
}
10198

10299
internal void Schedule(Action action)
@@ -122,7 +119,6 @@ static void Initialize(string path)
122119
PythonEngine.PythonHome = config.PythonHome;
123120
if (config.PythonHome != path)
124121
{
125-
var version = PythonEngine.Version;
126122
PythonEngine.PythonPath = EnvironmentHelper.GetPythonPath(config);
127123
}
128124
PythonEngine.Initialize();

src/Bonsai.Scripting.Python/Set.cs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,9 @@ public class Set : Sink
4545
/// </returns>
4646
public override IObservable<TSource> Process<TSource>(IObservable<TSource> source)
4747
{
48-
return RuntimeManager.RuntimeSource.SelectMany(runtime =>
49-
{
50-
return source.Do(value =>
51-
{
52-
using (Py.GIL())
53-
{
54-
var module = Module ?? runtime.MainModule;
55-
module.Set(VariableName, value);
56-
}
57-
});
58-
});
48+
return RuntimeManager.RuntimeSource
49+
.GetModuleOrDefaultAsync(Module)
50+
.SelectMany(module => source.Do(value => module.Set(VariableName, value)));
5951
}
6052
}
6153
}

0 commit comments

Comments
 (0)