Skip to content

Commit 3e2d69e

Browse files
committed
Return a pkl_exec_t instance when initialising the C library
This allows consumers to control how many instances they wish to have, and can also control the thread-local behaviour themselves.
1 parent 9de27fb commit 3e2d69e

File tree

5 files changed

+106
-65
lines changed

5 files changed

+106
-65
lines changed

libpkl/src/main/c/pkl.c

Lines changed: 60 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -24,97 +24,107 @@
2424

2525
#include <pkl.h>
2626

27-
#ifndef NULL
28-
#define NULL 0
29-
#endif
27+
void pkl_runtime_cleanup(pkl_exec_t *pexec) {
28+
pkl_internal_server_stop(pexec->graal_isolatethread);
29+
pkl_internal_close(pexec->graal_isolatethread);
30+
pexec->graal_isolatethread = NULL;
31+
}
3032

31-
pthread_mutex_t graal_mutex;
32-
graal_isolatethread_t *graal_isolatethread = NULL;
33+
pkl_exec_t *pkl_init(PklMessageResponseHandler handler, void *userData) {
34+
pkl_exec_t *pexec = calloc(1, sizeof(pkl_exec_t));
3335

34-
int pkl_attach_thread() {
35-
graal_isolate_t *graal_isolate = graal_get_isolate(graal_isolatethread);
36-
if (graal_isolate == NULL) {
37-
perror("pkl_attach_thread: couldn't get isolate");
38-
return -1;
36+
if (pexec == NULL) {
37+
perror("pkl_init: couldn't allocate pkl_exec_t");
38+
return NULL;
3939
}
4040

41-
if (graal_attach_thread(graal_isolate, &graal_isolatethread) != 0) {
42-
perror("pkl_attach_thread: couldn't attach thread");
43-
return -1;
41+
if (pthread_mutex_init(&pexec->graal_mutex, NULL) != 0) {
42+
perror("pkl_init: couldn't initialise pthread_mutex");
43+
pkl_runtime_cleanup(pexec);
44+
free(pexec);
45+
return NULL;
4446
}
4547

46-
return 0;
47-
}
48+
if (pthread_mutex_lock(&pexec->graal_mutex) != 0) {
49+
perror("pkl_init: couldn't lock mutex");
50+
pkl_runtime_cleanup(pexec);
51+
pthread_mutex_destroy(&pexec->graal_mutex);
52+
free(pexec);
53+
return NULL;
54+
}
4855

49-
int pkl_init(PklMessageResponseHandler handler, void *userData) {
50-
if (graal_isolatethread != NULL) {
51-
perror("pkl_init: graal_isolatethread is already initialised");
52-
return -1;
56+
pexec->graal_isolatethread = pkl_internal_init();
57+
pkl_internal_register_response_handler(pexec->graal_isolatethread, handler,
58+
userData);
59+
pkl_internal_server_start(pexec->graal_isolatethread);
60+
61+
if (pthread_mutex_unlock(&pexec->graal_mutex) != 0) {
62+
perror("pkl_init: couldn't unlock mutex");
63+
return NULL;
5364
}
5465

55-
if (pthread_mutex_init(&graal_mutex, NULL) != 0) {
56-
perror("pkl_init: couldn't initialise pthread_mutex");
66+
return pexec;
67+
};
68+
69+
int pkl_send_message(pkl_exec_t *pexec, int length, char *message) {
70+
if (pexec == NULL) {
71+
perror("pkl_send_message: pexec is NULL");
5772
return -1;
5873
}
5974

60-
if (pthread_mutex_lock(&graal_mutex) != 0) {
75+
if (message == NULL) {
76+
perror("pkl_send_message: message is NULL");
6177
return -1;
6278
}
6379

64-
graal_isolatethread = pkl_internal_init();
65-
pkl_internal_register_response_handler(graal_isolatethread, handler, userData);
66-
pkl_internal_server_start(graal_isolatethread);
67-
pthread_mutex_unlock(&graal_mutex);
68-
69-
return 0;
70-
};
71-
72-
int pkl_send_message(int length, char *message) {
73-
if (pthread_mutex_lock(&graal_mutex) != 0) {
80+
if (pthread_mutex_lock(&pexec->graal_mutex) != 0) {
81+
perror("pkl_send_message: couldn't lock mutex");
7482
return -1;
7583
}
7684

77-
if (pkl_attach_thread() != 0) {
78-
perror("pkl_send_message: couldn't attach thread");
85+
pkl_internal_send_message(pexec->graal_isolatethread, length, message);
86+
87+
if (pthread_mutex_unlock(&pexec->graal_mutex) != 0) {
88+
perror("pkl_send_message: couldn't unlock mutex");
7989
return -1;
8090
}
8191

82-
pkl_internal_send_message(graal_isolatethread, length, message);
83-
pthread_mutex_unlock(&graal_mutex);
84-
8592
return 0;
8693
};
8794

88-
int pkl_close() {
89-
if (pthread_mutex_lock(&graal_mutex) != 0) {
95+
int pkl_close(pkl_exec_t *pexec) {
96+
if (pexec == NULL) {
97+
perror("pkl_close: pexec is NULL");
9098
return -1;
9199
}
92100

93-
if (pkl_attach_thread() != 0) {
94-
perror("pkl_send_message: couldn't attach thread");
101+
if (pthread_mutex_lock(&pexec->graal_mutex) != 0) {
102+
perror("pkl_close: couldn't lock mutex");
95103
return -1;
96104
}
97105

98-
pkl_internal_server_stop(graal_isolatethread);
99-
pkl_internal_close(graal_isolatethread);
100-
graal_isolatethread = NULL;
106+
pkl_runtime_cleanup(pexec);
101107

102-
if (pthread_mutex_unlock(&graal_mutex) != 0) {
108+
if (pthread_mutex_unlock(&pexec->graal_mutex) != 0) {
109+
perror("pkl_close: couldn't unlock mutex");
103110
return -1;
104111
}
105112

106-
if (pthread_mutex_destroy(&graal_mutex) != 0) {
113+
if (pthread_mutex_destroy(&pexec->graal_mutex) != 0) {
114+
perror("pkl_close: couldn't destroy mutex");
107115
return -1;
108116
}
109117

118+
free(pexec);
119+
110120
return 0;
111121
};
112122

113-
char* pkl_version() {
114-
if (pkl_attach_thread() != 0) {
115-
perror("pkl_version: couldn't attach thread");
123+
char *pkl_version(pkl_exec_t *pexec) {
124+
if (pexec == NULL) {
125+
perror("pkl_version: pexec is NULL");
116126
return NULL;
117127
}
118128

119-
return pkl_internal_version(graal_isolatethread);
129+
return pkl_internal_version(pexec->graal_isolatethread);
120130
}

libpkl/src/main/c/pkl.h

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,22 @@
1515
*/
1616
// pkl.h
1717

18+
#include <pthread.h>
19+
#include <graal_isolate.h>
20+
21+
/**
22+
* Pkl executor instance that manages communication with the Pkl runtime.
23+
*
24+
* Instances should be created via pkl_init() and destroyed via pkl_close().
25+
*
26+
* All operations on this struct should be considered thread-safe due to
27+
* internal synchronization via the graal_mutex.
28+
*/
29+
typedef struct PKL_EXEC_T {
30+
pthread_mutex_t graal_mutex;
31+
graal_isolatethread_t *graal_isolatethread;
32+
} pkl_exec_t;
33+
1834
/**
1935
* The callback that gets called when a message is received from Pkl.
2036
*
@@ -30,30 +46,36 @@ typedef void (*PklMessageResponseHandler)(int length, char *message, void *userD
3046
* @param handler The callback that gets called when a message is received from Pkl.
3147
* @param userData User-defined data that gets passed to handler.
3248
*
33-
* @return -1 on failure, 0 on success.
49+
* @return NULL on failure, a pointer to a pkl_exec_t on success.
3450
*/
35-
int pkl_init(PklMessageResponseHandler handler, void *userData);
51+
pkl_exec_t *pkl_init(PklMessageResponseHandler handler, void *userData);
3652

3753
/**
3854
* Send a message to Pkl, providing the length and a pointer to the first byte.
3955
*
56+
* @param pexec The Pkl executor instance.
4057
* @param length The length of the message, in bytes.
4158
* @param message The message to send to Pkl.
4259
*
4360
* @return -1 on failure, 0 on success.
4461
*/
45-
int pkl_send_message(int length, char *message);
62+
int pkl_send_message(pkl_exec_t *pexec, int length, char *message);
4663

4764
/**
48-
* Cleans up any resources that were created as part of the `pkl_init` process.
65+
* Cleans up any resources that were created as part of the `pkl_init` process
66+
* for our `pkl_exec_t` instance.
67+
*
68+
* @param pexec The Pkl executor instance.
4969
*
5070
* @return -1 on failure, 0 on success.
5171
*/
52-
int pkl_close();
72+
int pkl_close(pkl_exec_t *pexec);
5373

5474
/**
5575
* Returns the version of Pkl in use.
5676
*
77+
* @param pexec The Pkl executor instance.
78+
*
5779
* @return a string with the version information.
5880
*/
59-
char* pkl_version();
81+
char *pkl_version(pkl_exec_t *pexec);

libpkl/src/nativeTest/kotlin/org/pkl/libpkl/LibPklJNA.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ interface LibPklJNA : Library {
3030
fun invoke(length: Int, message: Pointer, userData: Pointer?)
3131
}
3232

33-
fun pkl_init(handler: PklMessageResponseHandler, userData: Pointer?): Int
33+
fun pkl_init(handler: PklMessageResponseHandler, userData: Pointer?): Pointer?
3434

35-
fun pkl_send_message(length: Int, message: ByteArray): Int
35+
fun pkl_send_message(pexec: Pointer, length: Int, message: ByteArray): Int
3636

37-
fun pkl_close(): Int
37+
fun pkl_close(pexec: Pointer): Int
3838

39-
fun pkl_version(): String
39+
fun pkl_version(pexec: Pointer): String
4040
}

libpkl/src/nativeTest/kotlin/org/pkl/libpkl/LibPklMessageTransport.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,20 @@ class LibPklMessageTransport : MessageTransports.AbstractMessageTransport({}) {
3232
}
3333
}
3434

35+
var pexec: Pointer? = null
36+
3537
override fun doStart() {
36-
assertThat(LibPklJNA.INSTANCE.pkl_init(messageResponseHandler, Pointer.NULL)).isEqualTo(0)
38+
this.pexec = LibPklJNA.INSTANCE.pkl_init(messageResponseHandler, Pointer.NULL)
39+
assertThat(this.pexec).isNotNull()
3740
}
3841

3942
override fun doClose() {
40-
assertThat(LibPklJNA.INSTANCE.pkl_close()).isEqualTo(0)
43+
assertThat(LibPklJNA.INSTANCE.pkl_close(pexec!!)).isEqualTo(0)
4144
}
4245

4346
override fun doSend(message: Message) {
4447
val bytes = encode(message)
45-
LibPklJNA.INSTANCE.pkl_send_message(bytes.size, bytes)
48+
LibPklJNA.INSTANCE.pkl_send_message(pexec!!, bytes.size, bytes)
4649
}
4750

4851
private fun encode(message: Message): ByteArray {

libpkl/src/nativeTest/kotlin/org/pkl/libpkl/LibPklTest.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@ import org.pkl.core.Release
3737
class LibPklTest : AbstractServerTest() {
3838
override lateinit var client: TestTransport
3939

40+
val delegate = LibPklMessageTransport()
41+
4042
@BeforeEach
4143
fun beforeEach() {
42-
client = TestTransport(LibPklMessageTransport()).also { it.start() }
44+
client = TestTransport(delegate).also { it.start() }
4345
}
4446

4547
@AfterEach
@@ -49,6 +51,10 @@ class LibPklTest : AbstractServerTest() {
4951

5052
@Test
5153
fun testVersionString() {
52-
assertThat(LibPklJNA.INSTANCE.pkl_version()).isEqualTo(Release.current().version.toString())
54+
assertThat(delegate.pexec).isNotNull()
55+
56+
Release.current().version.toString().let { currentVersion ->
57+
assertThat(LibPklJNA.INSTANCE.pkl_version(delegate.pexec!!)).isEqualTo(currentVersion)
58+
}
5359
}
5460
}

0 commit comments

Comments
 (0)