1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 #include <atomic>
19 #include <stdio.h>
20 #include <string.h>
21
22 #include "messagequeue.h"
23
24 namespace nativemididemo {
25
26 static const int messageBufferSize = 64 * 1024;
27 static char messageBuffer[messageBufferSize];
28 static std::atomic_ullong messagesLastWritePosition;
29
writeMessage(const char * message)30 void writeMessage(const char* message)
31 {
32 static unsigned long long lastWritePos = 0;
33 size_t messageLen = strlen(message);
34 if (messageLen == 0) return;
35
36 messageLen += 1; // Also count in the null terminator.
37 char buffer[1024];
38 if (messageLen >= messageBufferSize) {
39 snprintf(buffer, sizeof(buffer), "!!! Message too long: %zu bytes !!!", messageLen);
40 message = buffer;
41 messageLen = strlen(message);
42 }
43
44 size_t wrappedWritePos = lastWritePos % messageBufferSize;
45 if (wrappedWritePos + messageLen >= messageBufferSize) {
46 size_t tailLen = messageBufferSize - wrappedWritePos;
47 memset(messageBuffer + wrappedWritePos, 0, tailLen);
48 lastWritePos += tailLen;
49 wrappedWritePos = 0;
50 }
51
52 memcpy(messageBuffer + wrappedWritePos, message, messageLen);
53 lastWritePos += messageLen;
54 messagesLastWritePosition.store(lastWritePos);
55 }
56
57 static char messageBufferCopy[messageBufferSize];
58
getRecentMessagesForJava(JNIEnv * env,jobject)59 jobjectArray getRecentMessagesForJava(JNIEnv* env, jobject)
60 {
61 static unsigned long long lastReadPos = 0;
62 const char* overrunMessage = "";
63 size_t messagesCount = 0;
64 jobjectArray result = NULL;
65
66 // First we copy the portion of the message buffer into messageBufferCopy. If after finishing
67 // the copy we notice that the writer has mutated the portion of the buffer that we were
68 // copying, we report an overrun. Afterwards we can safely read messages from the copy.
69 memset(messageBufferCopy, 0, sizeof(messageBufferCopy));
70 unsigned long long lastWritePos = messagesLastWritePosition.load();
71 if (lastWritePos - lastReadPos > messageBufferSize) {
72 overrunMessage = "!!! Message buffer overrun !!!";
73 messagesCount = 1;
74 lastReadPos = lastWritePos;
75 goto create_array;
76 }
77 if (lastWritePos == lastReadPos) return result;
78 if (lastWritePos / messageBufferSize == lastReadPos / messageBufferSize) {
79 size_t wrappedReadPos = lastReadPos % messageBufferSize;
80 memcpy(messageBufferCopy + wrappedReadPos,
81 messageBuffer + wrappedReadPos,
82 lastWritePos % messageBufferSize - wrappedReadPos);
83 } else {
84 size_t wrappedReadPos = lastReadPos % messageBufferSize;
85 memcpy(messageBufferCopy, messageBuffer, lastWritePos % messageBufferSize);
86 memcpy(messageBufferCopy + wrappedReadPos,
87 messageBuffer + wrappedReadPos,
88 messageBufferSize - wrappedReadPos);
89 }
90 {
91 unsigned long long newLastWritePos = messagesLastWritePosition.load();
92 if (newLastWritePos - lastReadPos > messageBufferSize) {
93 overrunMessage = "!!! Message buffer overrun !!!";
94 messagesCount = 1;
95 lastReadPos = lastWritePos = newLastWritePos;
96 goto create_array;
97 }
98 }
99 // Otherwise we ignore newLastWritePos, since we only have a copy of the buffer
100 // up to lastWritePos.
101
102 for (unsigned long long readPos = lastReadPos; readPos < lastWritePos; ) {
103 size_t messageLen = strlen(messageBufferCopy + (readPos % messageBufferSize));
104 if (messageLen != 0) {
105 readPos += messageLen + 1;
106 messagesCount++;
107 } else {
108 // Skip to the beginning of the buffer.
109 readPos = (readPos / messageBufferSize + 1) * messageBufferSize;
110 }
111 }
112 if (messagesCount == 0) {
113 lastReadPos = lastWritePos;
114 return result;
115 }
116
117 create_array:
118 result = env->NewObjectArray(
119 messagesCount, env->FindClass("java/lang/String"), env->NewStringUTF(overrunMessage));
120 if (lastWritePos == lastReadPos) return result;
121
122 jsize arrayIndex = 0;
123 while (lastReadPos < lastWritePos) {
124 size_t wrappedReadPos = lastReadPos % messageBufferSize;
125 if (messageBufferCopy[wrappedReadPos] != '\0') {
126 jstring message = env->NewStringUTF(messageBufferCopy + wrappedReadPos);
127 env->SetObjectArrayElement(result, arrayIndex++, message);
128 lastReadPos += env->GetStringLength(message) + 1;
129 env->DeleteLocalRef(message);
130 } else {
131 // Skip to the beginning of the buffer.
132 lastReadPos = (lastReadPos / messageBufferSize + 1) * messageBufferSize;
133 }
134 }
135 return result;
136 }
137
138 } // namespace nativemididemo
139