| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #include <atomic> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include "messagequeue.h" |
| |
| namespace nativemididemo { |
| |
| static const int messageBufferSize = 64 * 1024; |
| static char messageBuffer[messageBufferSize]; |
| static std::atomic_ullong messagesLastWritePosition; |
| |
| void writeMessage(const char* message) |
| { |
| static unsigned long long lastWritePos = 0; |
| size_t messageLen = strlen(message); |
| if (messageLen == 0) return; |
| |
| messageLen += 1; // Also count in the null terminator. |
| char buffer[1024]; |
| if (messageLen >= messageBufferSize) { |
| snprintf(buffer, sizeof(buffer), "!!! Message too long: %zu bytes !!!", messageLen); |
| message = buffer; |
| messageLen = strlen(message); |
| } |
| |
| size_t wrappedWritePos = lastWritePos % messageBufferSize; |
| if (wrappedWritePos + messageLen >= messageBufferSize) { |
| size_t tailLen = messageBufferSize - wrappedWritePos; |
| memset(messageBuffer + wrappedWritePos, 0, tailLen); |
| lastWritePos += tailLen; |
| wrappedWritePos = 0; |
| } |
| |
| memcpy(messageBuffer + wrappedWritePos, message, messageLen); |
| lastWritePos += messageLen; |
| messagesLastWritePosition.store(lastWritePos); |
| } |
| |
| static char messageBufferCopy[messageBufferSize]; |
| |
| jobjectArray getRecentMessagesForJava(JNIEnv* env, jobject) |
| { |
| static unsigned long long lastReadPos = 0; |
| const char* overrunMessage = ""; |
| size_t messagesCount = 0; |
| jobjectArray result = NULL; |
| |
| // First we copy the portion of the message buffer into messageBufferCopy. If after finishing |
| // the copy we notice that the writer has mutated the portion of the buffer that we were |
| // copying, we report an overrun. Afterwards we can safely read messages from the copy. |
| memset(messageBufferCopy, 0, sizeof(messageBufferCopy)); |
| unsigned long long lastWritePos = messagesLastWritePosition.load(); |
| if (lastWritePos - lastReadPos > messageBufferSize) { |
| overrunMessage = "!!! Message buffer overrun !!!"; |
| messagesCount = 1; |
| lastReadPos = lastWritePos; |
| goto create_array; |
| } |
| if (lastWritePos == lastReadPos) return result; |
| if (lastWritePos / messageBufferSize == lastReadPos / messageBufferSize) { |
| size_t wrappedReadPos = lastReadPos % messageBufferSize; |
| memcpy(messageBufferCopy + wrappedReadPos, |
| messageBuffer + wrappedReadPos, |
| lastWritePos % messageBufferSize - wrappedReadPos); |
| } else { |
| size_t wrappedReadPos = lastReadPos % messageBufferSize; |
| memcpy(messageBufferCopy, messageBuffer, lastWritePos % messageBufferSize); |
| memcpy(messageBufferCopy + wrappedReadPos, |
| messageBuffer + wrappedReadPos, |
| messageBufferSize - wrappedReadPos); |
| } |
| { |
| unsigned long long newLastWritePos = messagesLastWritePosition.load(); |
| if (newLastWritePos - lastReadPos > messageBufferSize) { |
| overrunMessage = "!!! Message buffer overrun !!!"; |
| messagesCount = 1; |
| lastReadPos = lastWritePos = newLastWritePos; |
| goto create_array; |
| } |
| } |
| // Otherwise we ignore newLastWritePos, since we only have a copy of the buffer |
| // up to lastWritePos. |
| |
| for (unsigned long long readPos = lastReadPos; readPos < lastWritePos; ) { |
| size_t messageLen = strlen(messageBufferCopy + (readPos % messageBufferSize)); |
| if (messageLen != 0) { |
| readPos += messageLen + 1; |
| messagesCount++; |
| } else { |
| // Skip to the beginning of the buffer. |
| readPos = (readPos / messageBufferSize + 1) * messageBufferSize; |
| } |
| } |
| if (messagesCount == 0) { |
| lastReadPos = lastWritePos; |
| return result; |
| } |
| |
| create_array: |
| result = env->NewObjectArray( |
| messagesCount, env->FindClass("java/lang/String"), env->NewStringUTF(overrunMessage)); |
| if (lastWritePos == lastReadPos) return result; |
| |
| jsize arrayIndex = 0; |
| while (lastReadPos < lastWritePos) { |
| size_t wrappedReadPos = lastReadPos % messageBufferSize; |
| if (messageBufferCopy[wrappedReadPos] != '\0') { |
| jstring message = env->NewStringUTF(messageBufferCopy + wrappedReadPos); |
| env->SetObjectArrayElement(result, arrayIndex++, message); |
| lastReadPos += env->GetStringLength(message) + 1; |
| env->DeleteLocalRef(message); |
| } else { |
| // Skip to the beginning of the buffer. |
| lastReadPos = (lastReadPos / messageBufferSize + 1) * messageBufferSize; |
| } |
| } |
| return result; |
| } |
| |
| } // namespace nativemididemo |