Skip to content

Commit 9c69339

Browse files
authored
Merge pull request #70 from edde746/fix/thread-safety
Add thread safety and lifecycle for native pointers
2 parents fa36424 + 09b36ca commit 9c69339

5 files changed

Lines changed: 169 additions & 30 deletions

File tree

lib_ass_kt/src/main/cpp/AssKt.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,12 @@ jlong nativeAssTrackInit(JNIEnv* env, jclass clazz, jlong ass) {
6969
}
7070

7171
jint nativeAssTrackGetWidth(JNIEnv* env, jclass clazz, jlong track) {
72+
if (!track) return 0;
7273
return ((ASS_Track *) track)->PlayResX;
7374
}
7475

7576
jobjectArray nativeAssTrackGetEvents(JNIEnv* env, jclass clazz, jlong track) {
77+
if (!track) return NULL;
7678
jclass eventClass = (*env)->FindClass(env, "io/github/peerless2012/ass/AssEvent");
7779
if (eventClass == NULL) {
7880
return NULL;
@@ -121,6 +123,7 @@ jobjectArray nativeAssTrackGetEvents(JNIEnv* env, jclass clazz, jlong track) {
121123
}
122124

123125
void nativeAssTrackClearEvents(JNIEnv* env, jclass clazz, jlong track) {
126+
if (!track) return;
124127
ASS_Track* tr = (ASS_Track *) track;
125128
for (int i = 0; i < tr->n_events; i++) {
126129
ass_free_event(tr, i);
@@ -129,10 +132,12 @@ void nativeAssTrackClearEvents(JNIEnv* env, jclass clazz, jlong track) {
129132
}
130133

131134
jint nativeAssTrackGetHeight(JNIEnv* env, jclass clazz, jlong track) {
135+
if (!track) return 0;
132136
return ((ASS_Track *) track)->PlayResY;
133137
}
134138

135139
void nativeAssTrackReadBuffer(JNIEnv* env, jclass clazz, jlong track, jbyteArray buffer, jint offset, jint length) {
140+
if (!track) return;
136141
jboolean isCopy;
137142
jbyte* elements = (*env)->GetByteArrayElements(env, buffer, &isCopy);
138143
if (elements == NULL) {
@@ -143,6 +148,7 @@ void nativeAssTrackReadBuffer(JNIEnv* env, jclass clazz, jlong track, jbyteArray
143148
}
144149

145150
void nativeAssTrackReadChunk(JNIEnv* env, jclass clazz, jlong track, jlong start, jlong duration, jbyteArray buffer, jint offset, jint length) {
151+
if (!track) return;
146152
jboolean isCopy;
147153
jbyte* elements = (*env)->GetByteArrayElements(env, buffer, &isCopy);
148154
if (elements == NULL) {
@@ -153,6 +159,7 @@ void nativeAssTrackReadChunk(JNIEnv* env, jclass clazz, jlong track, jlong start
153159
}
154160

155161
void nativeAssTrackDeinit(JNIEnv* env, jclass clazz, jlong track) {
162+
if (!track) return;
156163
ass_free_track((ASS_Track *) track);
157164
}
158165

@@ -175,18 +182,22 @@ jlong nativeAssRenderInit(JNIEnv* env, jclass clazz, jlong ass) {
175182
}
176183

177184
void nativeAssRenderSetFontScale(JNIEnv* env, jclass clazz, jlong render, jfloat scale) {
185+
if (!render) return;
178186
ass_set_font_scale((ASS_Renderer *) render, scale);
179187
}
180188

181189
void nativeAssRenderSetCacheLimit(JNIEnv* env, jclass clazz, jlong render, jint glyphMax, jint bitmapMaxSize) {
190+
if (!render) return;
182191
ass_set_cache_limits((ASS_Renderer *) render, glyphMax, bitmapMaxSize);
183192
}
184193

185194
void nativeAssRenderSetFrameSize(JNIEnv* env, jclass clazz, jlong render, jint width, jint height) {
195+
if (!render) return;
186196
ass_set_frame_size((ASS_Renderer *) render, width, height);
187197
}
188198

189199
void nativeAssRenderSetStorageSize(JNIEnv* env, jclass clazz, jlong render, jint width, jint height) {
200+
if (!render) return;
190201
ass_set_storage_size((ASS_Renderer *) render, width, height);
191202
}
192203

@@ -294,6 +305,7 @@ static int count_ass_images(ASS_Image *images) {
294305
}
295306

296307
jobject nativeAssRenderFrame(JNIEnv* env, jclass clazz, jlong render, jlong track, jlong time, jint type) {
308+
if (!render || !track) return NULL;
297309
int changed;
298310
ASS_Image *image = ass_render_frame((ASS_Renderer *) render, (ASS_Track *) track, time, &changed);
299311
if (image == NULL) {

lib_ass_kt/src/main/java/io/github/peerless2012/ass/Ass.kt

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package io.github.peerless2012.ass
22

3+
import java.util.concurrent.locks.ReentrantLock
4+
import kotlin.concurrent.withLock
5+
36
/**
47
* @Author peerless2012
58
* @Email peerless2012@126.com
@@ -29,26 +32,56 @@ class Ass {
2932

3033
}
3134

32-
private val nativeAss: Long = nativeAssInit()
35+
/** Single lock for all libass calls on this library instance. */
36+
val lock = ReentrantLock()
37+
38+
private var nativeAss: Long = nativeAssInit()
39+
40+
@Volatile
41+
var released = false
42+
private set
3343

3444
public fun createTrack(): AssTrack {
35-
return AssTrack(nativeAss)
45+
return lock.withLock {
46+
if (released || nativeAss == 0L) throw IllegalStateException("Ass already released")
47+
AssTrack(nativeAss, lock)
48+
}
3649
}
3750

3851
public fun createRender(): AssRender {
39-
return AssRender(nativeAss)
52+
return lock.withLock {
53+
if (released || nativeAss == 0L) throw IllegalStateException("Ass already released")
54+
AssRender(nativeAss, lock)
55+
}
4056
}
4157

4258
public fun addFont(name: String, buffer: ByteArray) {
43-
nativeAssAddFont(nativeAss, name, buffer)
59+
lock.withLock {
60+
if (released || nativeAss == 0L) return
61+
nativeAssAddFont(nativeAss, name, buffer)
62+
}
4463
}
4564

4665
public fun clearFont() {
47-
nativeAssClearFont(nativeAss)
66+
lock.withLock {
67+
if (released || nativeAss == 0L) return
68+
nativeAssClearFont(nativeAss)
69+
}
70+
}
71+
72+
fun release() {
73+
lock.withLock {
74+
if (released) return
75+
released = true
76+
if (nativeAss != 0L) {
77+
nativeAssDeinit(nativeAss)
78+
nativeAss = 0
79+
}
80+
}
4881
}
4982

5083
protected fun finalize() {
51-
nativeAssDeinit(nativeAss)
84+
release()
5285
}
5386

54-
}
87+
}

lib_ass_kt/src/main/java/io/github/peerless2012/ass/AssRender.kt

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package io.github.peerless2012.ass
22

3+
import java.util.concurrent.locks.ReentrantLock
4+
import kotlin.concurrent.withLock
5+
36
/**
47
* @Author peerless2012
58
* @Email peerless2012@126.com
69
* @DateTime 2025/Jan/05 14:18
710
* @Version V1.0
811
* @Description
912
*/
10-
class AssRender(nativeAss: Long) {
13+
class AssRender(nativeAss: Long, private val lock: ReentrantLock) {
1114

1215
companion object {
1316

@@ -33,36 +36,71 @@ class AssRender(nativeAss: Long) {
3336
external fun nativeAssRenderDeinit(render: Long)
3437
}
3538

36-
private val nativeRender: Long = nativeAssRenderInit(nativeAss)
39+
private var nativeRender: Long = nativeAssRenderInit(nativeAss)
40+
41+
@Volatile
42+
var released = false
43+
private set
3744

3845
private var track: AssTrack? = null
3946

4047
public fun setTrack(track: AssTrack?) {
41-
this.track = track
48+
lock.withLock {
49+
this.track = track
50+
}
4251
}
4352

4453
public fun setFontScale(scale: Float) {
45-
nativeAssRenderSetFontScale(nativeRender, scale)
54+
lock.withLock {
55+
if (released || nativeRender == 0L) return
56+
nativeAssRenderSetFontScale(nativeRender, scale)
57+
}
4658
}
4759

4860
public fun setCacheLimit(glyphMax: Int, bitmapMaxSize: Int) {
49-
nativeAssRenderSetCacheLimit(nativeRender, glyphMax, bitmapMaxSize)
61+
lock.withLock {
62+
if (released || nativeRender == 0L) return
63+
nativeAssRenderSetCacheLimit(nativeRender, glyphMax, bitmapMaxSize)
64+
}
5065
}
5166

5267
public fun setStorageSize(width: Int, height: Int) {
53-
nativeAssRenderSetStorageSize(nativeRender, width, height)
68+
lock.withLock {
69+
if (released || nativeRender == 0L) return
70+
nativeAssRenderSetStorageSize(nativeRender, width, height)
71+
}
5472
}
5573

5674
public fun setFrameSize(width: Int, height: Int) {
57-
nativeAssRenderSetFrameSize(nativeRender, width, height)
75+
lock.withLock {
76+
if (released || nativeRender == 0L) return
77+
nativeAssRenderSetFrameSize(nativeRender, width, height)
78+
}
5879
}
5980

6081
public fun renderFrame(time: Long, type: AssTexType): AssFrame? {
61-
return track?.let { nativeAssRenderFrame(nativeRender, it.nativeAssTrack, time, type.ordinal) }
82+
lock.withLock {
83+
if (released || nativeRender == 0L) return null
84+
val t = track ?: return null
85+
if (t.released || t.nativeAssTrack == 0L) return null
86+
return nativeAssRenderFrame(nativeRender, t.nativeAssTrack, time, type.ordinal)
87+
}
88+
}
89+
90+
fun release() {
91+
lock.withLock {
92+
if (released) return
93+
released = true
94+
track = null
95+
if (nativeRender != 0L) {
96+
nativeAssRenderDeinit(nativeRender)
97+
nativeRender = 0
98+
}
99+
}
62100
}
63101

64102
protected fun finalize() {
65-
nativeAssRenderDeinit(nativeRender)
103+
release()
66104
}
67105

68-
}
106+
}

lib_ass_kt/src/main/java/io/github/peerless2012/ass/AssTrack.kt

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package io.github.peerless2012.ass
22

3+
import java.util.concurrent.locks.ReentrantLock
4+
import kotlin.concurrent.withLock
5+
36
/**
47
* @Author peerless2012
58
* @Email peerless2012@126.com
69
* @DateTime 2025/Jan/05 14:18
710
* @Version V1.0
811
* @Description
912
*/
10-
class AssTrack(private val ass: Long) {
13+
class AssTrack(private val ass: Long, private val lock: ReentrantLock) {
1114

1215
companion object {
1316

@@ -36,34 +39,68 @@ class AssTrack(private val ass: Long) {
3639
external fun nativeAssTrackDeinit(track: Long)
3740
}
3841

39-
public val nativeAssTrack = nativeAssTrackInit(ass)
42+
var nativeAssTrack = nativeAssTrackInit(ass)
43+
private set
44+
45+
@Volatile
46+
var released = false
47+
private set
4048

4149
public fun getWidth(): Int {
42-
return nativeAssTrackGetWidth(nativeAssTrack)
50+
lock.withLock {
51+
if (released || nativeAssTrack == 0L) return 0
52+
return nativeAssTrackGetWidth(nativeAssTrack)
53+
}
4354
}
4455

4556
public fun getHeight(): Int {
46-
return nativeAssTrackGetHeight(nativeAssTrack)
57+
lock.withLock {
58+
if (released || nativeAssTrack == 0L) return 0
59+
return nativeAssTrackGetHeight(nativeAssTrack)
60+
}
4761
}
4862

4963
public fun getEvents(): Array<AssEvent>? {
50-
return nativeAssTrackGetEvents(nativeAssTrack)
64+
lock.withLock {
65+
if (released || nativeAssTrack == 0L) return null
66+
return nativeAssTrackGetEvents(nativeAssTrack)
67+
}
5168
}
5269

5370
public fun clearEvent() {
54-
nativeAssTrackClearEvents(nativeAssTrack)
71+
lock.withLock {
72+
if (released || nativeAssTrack == 0L) return
73+
nativeAssTrackClearEvents(nativeAssTrack)
74+
}
5575
}
5676

5777
public fun readBuffer(array: ByteArray, offset: Int = 0, length : Int = array.size) {
58-
nativeAssTrackReadBuffer(nativeAssTrack, array, offset, length)
78+
lock.withLock {
79+
if (released || nativeAssTrack == 0L) return
80+
nativeAssTrackReadBuffer(nativeAssTrack, array, offset, length)
81+
}
5982
}
6083

6184
public fun readChunk(start: Long, duration: Long, array: ByteArray, offset: Int = 0, length: Int = array.size) {
62-
nativeAssTrackReadChunk(nativeAssTrack, start, duration, array, offset, length)
85+
lock.withLock {
86+
if (released || nativeAssTrack == 0L) return
87+
nativeAssTrackReadChunk(nativeAssTrack, start, duration, array, offset, length)
88+
}
89+
}
90+
91+
fun release() {
92+
lock.withLock {
93+
if (released) return
94+
released = true
95+
if (nativeAssTrack != 0L) {
96+
nativeAssTrackDeinit(nativeAssTrack)
97+
nativeAssTrack = 0
98+
}
99+
}
63100
}
64101

65102
protected fun finalize() {
66-
nativeAssTrackDeinit(nativeAssTrack)
103+
release()
67104
}
68105

69-
}
106+
}

0 commit comments

Comments
 (0)