Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added OS X ChangeMonitor support; fixed memory leak in Platform::GetFiles. #16

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions include/Bootil/File/Changes.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

#include "Bootil/Bootil.h"

#if defined( __APPLE__ ) && defined( __MACH__ )
#include <MacTypes.h>
struct __FSEventStream; // Forward declaration
#endif

namespace Bootil
{
namespace File
Expand Down Expand Up @@ -50,8 +55,12 @@ namespace Bootil
void CheckForChanges();
void StartWatch();

#ifdef __linux__
int m_inotify;
#if defined( __APPLE__ ) && defined( __MACH__ )
static void HandleFSEvent( const __FSEventStream *stream, void *ctx, size_t numEvents, void *eventPaths, const UInt32 evFlags[], const UInt64 evIds[] );

__FSEventStream* m_fsStreamRef;
#elif defined(__linux__)
int m_inotify;
#endif
void* m_dirHandles;
BString m_strFolderName;
Expand Down
124 changes: 101 additions & 23 deletions src/Bootil/File/Changes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
#define _WIN32_WINNT 0x0502

#include <windows.h>
#elif __linux__
#elif defined( __APPLE__ ) && defined( __MACH__ )
#include <CoreServices/CoreServices.h>
#include <libgen.h>
#if MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_7
#define kFSEventStreamCreateFlagFileEvents 0x00000010
#endif
#elif defined( __linux__ )
#include <unistd.h>
#include <sys/select.h>
#include <sys/inotify.h>
Expand All @@ -17,7 +23,7 @@ namespace
#else
const char separator = '/';
#endif

struct WatcherData
{
Bootil::BString directory;
Expand All @@ -27,12 +33,12 @@ namespace
OVERLAPPED overlapped;

char buffer[1024];
#elif __linux__
#elif defined( __linux__ )
int handle;
#endif
};

#ifdef __linux__
#if defined( __linux__ )
struct WatcherFind
{
int handle;
Expand All @@ -41,6 +47,7 @@ namespace
};
#endif

#if !defined( __APPLE__ ) || !defined( __MACH__ ) // if not OS X
void RecurseDirectories( Bootil::String::List & directories, const Bootil::BString & path )
{
Bootil::String::List currentDirectory;
Expand All @@ -53,6 +60,7 @@ namespace
RecurseDirectories( directories, directoryPath );
}
}
#endif
}

namespace Bootil
Expand All @@ -62,41 +70,99 @@ namespace Bootil
ChangeMonitor::ChangeMonitor()
{
m_dirHandles = NULL;
#ifdef __linux__
#if defined( __APPLE__ ) && defined( __MACH__ )
m_fsStreamRef = NULL;
#elif defined( __linux__ )
m_inotify = inotify_init();
#endif
}

ChangeMonitor::~ChangeMonitor()
{
Stop();
#ifdef __linux__
close(m_inotify);
#if defined( __linux__ )
close( m_inotify );
#endif
}

#if defined( __APPLE__ ) && defined( __MACH__ )
void ChangeMonitor::HandleFSEvent( const __FSEventStream *stream, void *ctx, size_t numEvents, void *eventPaths, const UInt32 evFlags[], const UInt64 evIds[] )
{
char **paths = ( char ** )eventPaths;
for ( size_t i = 0; i < numEvents; i++ )
{
ChangeMonitor *monitor = ( ChangeMonitor * )ctx;
char *path = paths[i];
if ( strcmp ( dirname( path ), monitor->m_strFolderName.c_str() ) != 0
&& strcmp ( path, monitor->m_strFolderName.c_str() ) != 0
&& !monitor->m_bWatchSubtree )
{
continue;
}

// Found a changed file. Get the relative path to it.

BString strPath( path );

if ( strPath == monitor->m_strFolderName )
{
strPath = "";
}
else
{
strPath = strPath.substr( monitor->m_strFolderName.size() + 1 );
}

// Add it to the change list.

monitor->NoteFileChanged( path );
}
}
#endif

bool ChangeMonitor::WatchFolder( const BString & strFolder, bool bWatchSubtree )
{
Stop();

m_strFolderName = strFolder;
Bootil::String::Util::TrimRight( m_strFolderName, &separator );
Bootil::String::Util::FindAndReplace( m_strFolderName, BString( &separator ).append( &separator ), &separator );

m_bWatchSubtree = bWatchSubtree;

#if defined( __APPLE__ ) && defined( __MACH__ )
if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_7)
{
Output::Msg("ChangeMonitor requires OS X 10.7 or later.\n");
return false;
}

CFStringRef dir = CFStringCreateWithCString( NULL, m_strFolderName.c_str(), kCFStringEncodingUTF8 );
CFArrayRef paths = CFArrayCreate( NULL, ( const void** )&dir, 1, NULL );
FSEventStreamContext ctx = { 0, this, NULL, NULL, NULL };
m_fsStreamRef = FSEventStreamCreate( NULL, &ChangeMonitor::HandleFSEvent, &ctx, paths, kFSEventStreamEventIdSinceNow, 0.1, kFSEventStreamCreateFlagFileEvents );
FSEventStreamScheduleWithRunLoop( m_fsStreamRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
FSEventStreamStart( m_fsStreamRef );

CFRelease( paths );
CFRelease( dir );
#else

m_dirHandles = new std::vector<WatcherData>;

// Watch the main folder.

WatcherData watcherData;

watcherData.directory = strFolder;
watcherData.directory = m_strFolderName;
#ifdef _WIN32
watcherData.directoryHandle = CreateFileA( watcherData.directory.c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL );

memset( &watcherData.overlapped, 0, sizeof( watcherData.overlapped ) );
watcherData.overlapped.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );

std::fill( watcherData.buffer, watcherData.buffer + 1024, 0 );
#elif __linux__
#elif defined( __linux__ )
int flags = IN_ATTRIB | IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO;
watcherData.handle = inotify_add_watch( m_inotify, watcherData.directory.c_str(), flags );

Expand All @@ -111,7 +177,7 @@ namespace Bootil
if ( m_bWatchSubtree )
{
Bootil::String::List directories;
RecurseDirectories( directories, strFolder );
RecurseDirectories( directories, m_strFolderName );
BOOTIL_FOREACH_CONST( directory, directories, Bootil::String::List )
{
WatcherData watcherData;
Expand All @@ -131,7 +197,7 @@ namespace Bootil
watcherData.overlapped.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );

std::fill( watcherData.buffer, watcherData.buffer + 1024, 0 );
#elif __linux__
#elif defined( __linux__ )
watcherData.handle = inotify_add_watch( m_inotify, watcherData.directory.c_str(), flags );

if ( watcherData.handle < 0 )
Expand All @@ -141,7 +207,7 @@ namespace Bootil
( ( std::vector<WatcherData>* ) m_dirHandles )->push_back( watcherData );
}
}

#endif
StartWatch();

return true;
Expand All @@ -158,19 +224,28 @@ namespace Bootil

void ChangeMonitor::Stop()
{
#if defined( __APPLE__ ) && defined( __MACH__ )
if ( m_fsStreamRef )
{
FSEventStreamStop( m_fsStreamRef );
FSEventStreamInvalidate( m_fsStreamRef );
FSEventStreamRelease( m_fsStreamRef );
m_fsStreamRef = NULL;
}
#else
if ( !m_dirHandles ) { return; }

BOOTIL_FOREACH ( watcherData, ( *( std::vector<WatcherData>* ) m_dirHandles ), std::vector<WatcherData> )
BOOTIL_FOREACH( watcherData, ( *( std::vector<WatcherData>* ) m_dirHandles ), std::vector<WatcherData> )
{
#ifdef _WIN32
CloseHandle( watcherData->directoryHandle );
watcherData->directoryHandle = NULL;
#elif __linux__
#elif defined( __linux__ )
inotify_rm_watch( m_inotify, watcherData->handle );
#endif
}

delete ( std::vector<WatcherData>* ) m_dirHandles;
#endif
}

bool ChangeMonitor::HasChanges()
Expand All @@ -190,10 +265,13 @@ namespace Bootil

void ChangeMonitor::CheckForChanges()
{
#if defined( __APPLE__ ) && defined( __MACH__ )
CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0.01, true );
#else
if ( !m_dirHandles ) { return; }

#ifdef _WIN32
BOOTIL_FOREACH ( watcherData, ( *( std::vector<WatcherData>* ) m_dirHandles ), std::vector<WatcherData> )
BOOTIL_FOREACH( watcherData, ( *( std::vector<WatcherData>* ) m_dirHandles ), std::vector<WatcherData> )
{
DWORD dwNumberBytes = 0;

Expand All @@ -208,7 +286,7 @@ namespace Bootil
{
FILE_NOTIFY_INFORMATION* pNotify = ( FILE_NOTIFY_INFORMATION* ) &watcherData->buffer[iOffset];
iOffset += pNotify->NextEntryOffset;

if ( pNotify->FileNameLength > 0 )
{
// Found a changed file. Get the relative path to it.
Expand All @@ -221,7 +299,7 @@ namespace Bootil
}
else
{
path = watcherData->directory.substr( m_strFolderName.size() + 1 ) + separator;
path = watcherData->directory.substr( m_strFolderName.size() + 1 ) + separator;
}

path += Bootil::String::Convert::FromWide( WString( pNotify->FileName, pNotify->FileNameLength / sizeof( wchar_t ) ) );
Expand All @@ -246,14 +324,14 @@ namespace Bootil
ReadDirectoryChangesW( watcherData->directoryHandle, &watcherData->buffer, sizeof( watcherData->buffer ), m_bWatchSubtree, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_LAST_WRITE, NULL, &watcherData->overlapped, NULL );
}
}
#elif __linux__
#elif defined( __linux__ )
timeval tv;
tv.tv_sec = tv.tv_usec = 0;

fd_set fds;
FD_ZERO( &fds );
FD_SET( m_inotify, &fds );

select( m_inotify + 1, &fds, NULL, NULL, &tv );

if ( FD_ISSET( m_inotify, &fds ) )
Expand All @@ -271,7 +349,7 @@ namespace Bootil
std::vector<WatcherData>& watches = *( std::vector<WatcherData>* ) m_dirHandles;
WatcherFind find = {};
find.handle = event->wd;
std::vector<WatcherData>::const_iterator it = std::find_if(watches.begin(), watches.end(), find);
std::vector<WatcherData>::const_iterator it = std::find_if( watches.begin(), watches.end(), find );

if ( event->len > 0 && it != watches.end() )
{
Expand All @@ -281,13 +359,14 @@ namespace Bootil
i += sizeof( inotify_event ) + event->len;
}
}
#endif
#endif
}

void ChangeMonitor::StartWatch()
{
#ifdef _WIN32
BOOTIL_FOREACH ( watcherData, ( *( std::vector<WatcherData>* ) m_dirHandles ), std::vector<WatcherData> )
BOOTIL_FOREACH( watcherData, ( *( std::vector<WatcherData>* ) m_dirHandles ), std::vector<WatcherData> )
{
std::fill( watcherData->buffer, watcherData->buffer + 1024, 0 );

Expand All @@ -299,4 +378,3 @@ namespace Bootil
}
}
}

9 changes: 5 additions & 4 deletions src/Bootil/Platform/Platform_LINUX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,11 @@ namespace Bootil

BOOTIL_EXPORT int FindFiles( String::List* files, String::List* folders, const BString & strFind, bool bUpUpFolders )
{
BString dirName = strdup( strFind.c_str() );
dirName = dirname( ( char* )dirName.c_str() );
BString findName = strdup( strFind.c_str() );
findName = basename( ( char* )findName.c_str() );
std::vector<char> tmp( strFind.c_str(), strFind.c_str() + strFind.size() + 1 );
BString dirName( dirname( &tmp[0] ) );
tmp = std::vector<char>( strFind.c_str(), strFind.c_str() + strFind.size() + 1 );
BString findName( basename( &tmp[0] ) );

DIR* dp;
dirent* dirp;
int iFiles = 0;
Expand Down
9 changes: 5 additions & 4 deletions src/Bootil/Platform/Platform_OSX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,11 @@ namespace Bootil

BOOTIL_EXPORT int FindFiles( String::List* files, String::List* folders, const BString & strFind, bool bUpUpFolders )
{
BString dirName = strdup( strFind.c_str() );
dirName = dirname( ( char* )dirName.c_str() );
BString findName = strdup( strFind.c_str() );
findName = basename( ( char* )findName.c_str() );
std::vector<char> tmp( strFind.c_str(), strFind.c_str() + strFind.size() + 1 );
BString dirName( dirname( &tmp[0] ) );
tmp = std::vector<char>( strFind.c_str(), strFind.c_str() + strFind.size() + 1 );
BString findName( basename( &tmp[0] ) );

DIR* dp;
dirent* dirp;
int iFiles = 0;
Expand Down