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 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 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