Browse Source

Assorted threading and undefined behaviour fixes

tags/2021-05-28
Tom Poole 8 years ago
parent
commit
8cecf0baf9
12 changed files with 121 additions and 42 deletions
  1. +7
    -5
      modules/juce_analytics/destinations/juce_ThreadedAnalyticsDestination.cpp
  2. +12
    -8
      modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
  3. +1
    -1
      modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h
  4. +1
    -1
      modules/juce_core/threads/juce_SpinLock.h
  5. +72
    -13
      modules/juce_core/zip/juce_ZipFile.cpp
  6. +4
    -4
      modules/juce_events/messages/juce_MessageManager.cpp
  7. +2
    -2
      modules/juce_events/messages/juce_MessageManager.h
  8. +3
    -3
      modules/juce_events/native/juce_ios_MessageManager.mm
  9. +3
    -3
      modules/juce_events/native/juce_mac_MessageManager.mm
  10. +7
    -0
      modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp
  11. +1
    -1
      modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.h
  12. +8
    -1
      modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp

+ 7
- 5
modules/juce_analytics/destinations/juce_ThreadedAnalyticsDestination.cpp View File

@@ -176,7 +176,7 @@ namespace DestinationTestHelpers
std::deque<AnalyticsEvent>& unloggedEvents) std::deque<AnalyticsEvent>& unloggedEvents)
: TestDestination (loggedEvents, unloggedEvents) : TestDestination (loggedEvents, unloggedEvents)
{ {
startAnalyticsThread (100);
startAnalyticsThread (20);
} }
virtual ~BasicDestination() virtual ~BasicDestination()
@@ -303,12 +303,14 @@ struct ThreadedAnalyticsDestinationTests : public UnitTest
beginTest ("Basic"); beginTest ("Basic");
{ {
DestinationTestHelpers::BasicDestination destination (loggedEvents, unloggedEvents);
{
DestinationTestHelpers::BasicDestination destination (loggedEvents, unloggedEvents);
for (auto& event : testEvents)
destination.logEvent (event);
for (auto& event : testEvents)
destination.logEvent (event);
Thread::sleep (400);
Thread::sleep (400);
}
compareEventQueues (loggedEvents, testEvents); compareEventQueues (loggedEvents, testEvents);
expect (unloggedEvents.size() == 0); expect (unloggedEvents.size() == 0);


+ 12
- 8
modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp View File

@@ -932,6 +932,8 @@ AudioDeviceManager::LevelMeter::LevelMeter() noexcept : level() {}
void AudioDeviceManager::LevelMeter::updateLevel (const float* const* channelData, int numChannels, int numSamples) noexcept void AudioDeviceManager::LevelMeter::updateLevel (const float* const* channelData, int numChannels, int numSamples) noexcept
{ {
auto localLevel = level.get();
if (enabled.get() != 0 && numChannels > 0) if (enabled.get() != 0 && numChannels > 0)
{ {
for (int j = 0; j < numSamples; ++j) for (int j = 0; j < numSamples; ++j)
@@ -943,20 +945,22 @@ void AudioDeviceManager::LevelMeter::updateLevel (const float* const* channelDat
s /= (float) numChannels; s /= (float) numChannels;
const double decayFactor = 0.99992;
const float decayFactor = 0.99992f;
if (s > level)
level = s;
else if (level > 0.001f)
level *= decayFactor;
if (s > localLevel)
localLevel = s;
else if (localLevel > 0.001f)
localLevel *= decayFactor;
else else
level = 0;
localLevel = 0;
} }
} }
else else
{ {
level = 0;
localLevel = 0;
} }
level = localLevel;
} }
void AudioDeviceManager::LevelMeter::setEnabled (bool shouldBeEnabled) noexcept void AudioDeviceManager::LevelMeter::setEnabled (bool shouldBeEnabled) noexcept
@@ -968,7 +972,7 @@ void AudioDeviceManager::LevelMeter::setEnabled (bool shouldBeEnabled) noexcept
double AudioDeviceManager::LevelMeter::getCurrentLevel() const noexcept double AudioDeviceManager::LevelMeter::getCurrentLevel() const noexcept
{ {
jassert (enabled.get() != 0); // you need to call setEnabled (true) before using this! jassert (enabled.get() != 0); // you need to call setEnabled (true) before using this!
return level;
return level.get();
} }
void AudioDeviceManager::playTestSound() void AudioDeviceManager::playTestSound()


+ 1
- 1
modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h View File

@@ -488,7 +488,7 @@ private:
double getCurrentLevel() const noexcept; double getCurrentLevel() const noexcept;
Atomic<int> enabled; Atomic<int> enabled;
double level;
Atomic<float> level;
}; };
LevelMeter inputLevelMeter, outputLevelMeter; LevelMeter inputLevelMeter, outputLevelMeter;


+ 1
- 1
modules/juce_core/threads/juce_SpinLock.h View File

@@ -62,7 +62,7 @@ public:
/** Releases the lock. */ /** Releases the lock. */
inline void exit() const noexcept inline void exit() const noexcept
{ {
jassert (lock.value == 1); // Agh! Releasing a lock that isn't currently held!
jassert (lock.get() == 1); // Agh! Releasing a lock that isn't currently held!
lock = 0; lock = 0;
} }


+ 72
- 13
modules/juce_core/zip/juce_ZipFile.cpp View File

@@ -23,17 +23,30 @@
namespace juce namespace juce
{ {
uint16 readUnalignedLittleEndianShort (const void* buffer)
{
auto data = readUnaligned<uint16> (buffer);
return ByteOrder::littleEndianShort (&data);
}
uint32 readUnalignedLittleEndianInt (const void* buffer)
{
auto data = readUnaligned<uint32> (buffer);
return ByteOrder::littleEndianInt (&data);
}
struct ZipFile::ZipEntryHolder struct ZipFile::ZipEntryHolder
{ {
ZipEntryHolder (const char* buffer, int fileNameLen) ZipEntryHolder (const char* buffer, int fileNameLen)
{ {
isCompressed = ByteOrder::littleEndianShort (buffer + 10) != 0;
entry.fileTime = parseFileTime (ByteOrder::littleEndianShort (buffer + 12),
ByteOrder::littleEndianShort (buffer + 14));
compressedSize = (int64) ByteOrder::littleEndianInt (buffer + 20);
entry.uncompressedSize = (int64) ByteOrder::littleEndianInt (buffer + 24);
streamOffset = (int64) ByteOrder::littleEndianInt (buffer + 42);
entry.filename = String::fromUTF8 (buffer + 46, fileNameLen);
isCompressed = readUnalignedLittleEndianShort (buffer + 10) != 0;
entry.fileTime = parseFileTime (readUnalignedLittleEndianShort (buffer + 12),
readUnalignedLittleEndianShort (buffer + 14));
compressedSize = (int64) readUnalignedLittleEndianInt (buffer + 20);
entry.uncompressedSize = (int64) readUnalignedLittleEndianInt (buffer + 24);
streamOffset = (int64) readUnalignedLittleEndianInt (buffer + 42);
entry.filename = String::fromUTF8 (buffer + 46, fileNameLen);
} }
static Time parseFileTime (uint32 time, uint32 date) noexcept static Time parseFileTime (uint32 time, uint32 date) noexcept
@@ -74,12 +87,12 @@ static int64 findCentralDirectoryFileHeader (InputStream& input, int& numEntries
for (int i = 0; i < 22; ++i) for (int i = 0; i < 22; ++i)
{ {
if (ByteOrder::littleEndianInt (buffer + i) == 0x06054b50)
if (readUnalignedLittleEndianInt (buffer + i) == 0x06054b50)
{ {
in.setPosition (pos + i); in.setPosition (pos + i);
in.read (buffer, 22); in.read (buffer, 22);
numEntries = ByteOrder::littleEndianShort (buffer + 10);
auto offset = (int64) ByteOrder::littleEndianInt (buffer + 16);
numEntries = readUnalignedLittleEndianShort (buffer + 10);
auto offset = (int64) readUnalignedLittleEndianInt (buffer + 16);
if (offset >= 4) if (offset >= 4)
{ {
@@ -351,7 +364,7 @@ void ZipFile::init()
break; break;
auto* buffer = static_cast<const char*> (headerData.getData()) + pos; auto* buffer = static_cast<const char*> (headerData.getData()) + pos;
auto fileNameLen = ByteOrder::littleEndianShort (buffer + 28);
auto fileNameLen = readUnalignedLittleEndianShort (buffer + 28);
if (pos + 46 + fileNameLen > size) if (pos + 46 + fileNameLen > size)
break; break;
@@ -359,8 +372,8 @@ void ZipFile::init()
entries.add (new ZipEntryHolder (buffer, fileNameLen)); entries.add (new ZipEntryHolder (buffer, fileNameLen));
pos += 46 + fileNameLen pos += 46 + fileNameLen
+ ByteOrder::littleEndianShort (buffer + 30)
+ ByteOrder::littleEndianShort (buffer + 32);
+ readUnalignedLittleEndianShort (buffer + 30)
+ readUnalignedLittleEndianShort (buffer + 32);
} }
} }
} }
@@ -600,4 +613,50 @@ bool ZipFile::Builder::writeToStream (OutputStream& target, double* const progre
return true; return true;
} }
//==============================================================================
#if JUCE_UNIT_TESTS
struct ZIPTests : public UnitTest
{
ZIPTests() : UnitTest ("ZIP") {}
void runTest() override
{
beginTest ("ZIP");
ZipFile::Builder builder;
StringArray entryNames { "first", "second", "third" };
HashMap<String, MemoryBlock> blocks;
for (auto& entryName : entryNames)
{
auto& block = blocks.getReference (entryName);
MemoryOutputStream mo (block, false);
mo << entryName;
mo.flush();
builder.addEntry (new MemoryInputStream (block, false), 9, entryName, Time::getCurrentTime());
}
MemoryBlock data;
MemoryOutputStream mo (data, false);
builder.writeToStream (mo, nullptr);
MemoryInputStream mi (data, false);
ZipFile zip (mi);
expectEquals (zip.getNumEntries(), entryNames.size());
for (auto& entryName : entryNames)
{
auto* entry = zip.getEntry (entryName);
ScopedPointer<InputStream> input (zip.createStreamForEntry (*entry));
expectEquals (input->readEntireStreamAsString(), entryName);
}
}
};
static ZIPTests zipTests;
#endif
} // namespace juce } // namespace juce

+ 4
- 4
modules/juce_events/messages/juce_MessageManager.cpp View File

@@ -68,7 +68,7 @@ bool MessageManager::MessageBase::post()
{ {
auto* mm = MessageManager::instance; auto* mm = MessageManager::instance;
if (mm == nullptr || mm->quitMessagePosted || ! postMessageToSystemQueue (this))
if (mm == nullptr || mm->quitMessagePosted.get() != 0 || ! postMessageToSystemQueue (this))
{ {
Ptr deleter (this); // (this will delete messages that were just created with a 0 ref count) Ptr deleter (this); // (this will delete messages that were just created with a 0 ref count)
return false; return false;
@@ -85,7 +85,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
auto endTime = Time::currentTimeMillis() + millisecondsToRunFor; auto endTime = Time::currentTimeMillis() + millisecondsToRunFor;
while (! quitMessageReceived)
while (quitMessageReceived.get() == 0)
{ {
JUCE_TRY JUCE_TRY
{ {
@@ -98,7 +98,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
break; break;
} }
return ! quitMessageReceived;
return quitMessageReceived.get() == 0;
} }
#endif #endif
@@ -121,7 +121,7 @@ void MessageManager::runDispatchLoop()
{ {
jassert (isThisTheMessageThread()); // must only be called by the message thread jassert (isThisTheMessageThread()); // must only be called by the message thread
while (! quitMessageReceived)
while (quitMessageReceived.get() == 0)
{ {
JUCE_TRY JUCE_TRY
{ {


+ 2
- 2
modules/juce_events/messages/juce_MessageManager.h View File

@@ -80,7 +80,7 @@ public:
/** Returns true if the stopDispatchLoop() method has been called. /** Returns true if the stopDispatchLoop() method has been called.
*/ */
bool hasStopMessageBeenSent() const noexcept { return quitMessagePosted; }
bool hasStopMessageBeenSent() const noexcept { return quitMessagePosted.get() != 0; }
#if JUCE_MODAL_LOOPS_PERMITTED || DOXYGEN #if JUCE_MODAL_LOOPS_PERMITTED || DOXYGEN
/** Synchronously dispatches messages until a given time has elapsed. /** Synchronously dispatches messages until a given time has elapsed.
@@ -318,7 +318,7 @@ private:
friend class MessageManagerLock; friend class MessageManagerLock;
ScopedPointer<ActionBroadcaster> broadcaster; ScopedPointer<ActionBroadcaster> broadcaster;
bool quitMessagePosted = false, quitMessageReceived = false;
Atomic<int> quitMessagePosted { 0 }, quitMessageReceived { 0 };
Thread::ThreadID messageThreadId; Thread::ThreadID messageThreadId;
Atomic<Thread::ThreadID> threadWithLock; Atomic<Thread::ThreadID> threadWithLock;


+ 3
- 3
modules/juce_events/native/juce_ios_MessageManager.mm View File

@@ -27,7 +27,7 @@ void MessageManager::runDispatchLoop()
{ {
jassert (isThisTheMessageThread()); // must only be called by the message thread jassert (isThisTheMessageThread()); // must only be called by the message thread
while (! quitMessagePosted)
while (quitMessagePosted.get() == 0)
{ {
JUCE_AUTORELEASEPOOL JUCE_AUTORELEASEPOOL
{ {
@@ -55,7 +55,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
uint32 startTime = Time::getMillisecondCounter(); uint32 startTime = Time::getMillisecondCounter();
NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001]; NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001];
while (! quitMessagePosted)
while (quitMessagePosted.get() == 0)
{ {
JUCE_AUTORELEASEPOOL JUCE_AUTORELEASEPOOL
{ {
@@ -68,7 +68,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
} }
} }
return ! quitMessagePosted;
return quitMessagePosted.get() == 0;
} }
} }
#endif #endif


+ 3
- 3
modules/juce_events/native/juce_mac_MessageManager.mm View File

@@ -313,7 +313,7 @@ private:
//============================================================================== //==============================================================================
void MessageManager::runDispatchLoop() void MessageManager::runDispatchLoop()
{ {
if (! quitMessagePosted) // check that the quit message wasn't already posted..
if (quitMessagePosted.get() == 0) // check that the quit message wasn't already posted..
{ {
JUCE_AUTORELEASEPOOL JUCE_AUTORELEASEPOOL
{ {
@@ -383,7 +383,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
uint32 endTime = Time::getMillisecondCounter() + (uint32) millisecondsToRunFor; uint32 endTime = Time::getMillisecondCounter() + (uint32) millisecondsToRunFor;
while (! quitMessagePosted)
while (quitMessagePosted.get() == 0)
{ {
JUCE_AUTORELEASEPOOL JUCE_AUTORELEASEPOOL
{ {
@@ -402,7 +402,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
} }
} }
return ! quitMessagePosted;
return quitMessagePosted.get() == 0;
} }
#endif #endif


+ 7
- 0
modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp View File

@@ -120,6 +120,13 @@ void DirectoryContentsList::setFileFilter (const FileFilter* newFileFilter)
} }
//============================================================================== //==============================================================================
int DirectoryContentsList::getNumFiles() const noexcept
{
const ScopedLock sl (fileListLock);
return files.size();
}
bool DirectoryContentsList::getFileInfo (const int index, FileInfo& result) const bool DirectoryContentsList::getFileInfo (const int index, FileInfo& result) const
{ {
const ScopedLock sl (fileListLock); const ScopedLock sl (fileListLock);


+ 1
- 1
modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.h View File

@@ -163,7 +163,7 @@ public:
@see getFileInfo, getFile @see getFileInfo, getFile
*/ */
int getNumFiles() const noexcept { return files.size(); }
int getNumFiles() const noexcept;
/** Returns the cached information about one of the files in the list. /** Returns the cached information about one of the files in the list.


+ 8
- 1
modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp View File

@@ -177,6 +177,8 @@ public:
void paintItem (Graphics& g, int width, int height) override void paintItem (Graphics& g, int width, int height) override
{ {
ScopedLock lock (iconUpdate);
if (file != File()) if (file != File())
{ {
updateIcon (true); updateIcon (true);
@@ -229,6 +231,7 @@ private:
OptionalScopedPointer<DirectoryContentsList> subContentsList; OptionalScopedPointer<DirectoryContentsList> subContentsList;
bool isDirectory; bool isDirectory;
TimeSliceThread& thread; TimeSliceThread& thread;
CriticalSection iconUpdate;
Image icon; Image icon;
String fileSize, modTime; String fileSize, modTime;
@@ -249,7 +252,11 @@ private:
if (im.isValid()) if (im.isValid())
{ {
icon = im;
{
ScopedLock lock (iconUpdate);
icon = im;
}
triggerAsyncUpdate(); triggerAsyncUpdate();
} }
} }


Loading…
Cancel
Save