Home | History | Annotate | Download | only in jni
      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