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