Review Board

1.0

NSSound rework

Updated 1 year, 1 month ago

Stef Reviewers
EtoileCore
None GNUstep (gnustep)
This is a very intrusive NSSound patch and should be reviewed carefully.  Almost all of NSSound was touched and this new implementation is no longer compatible.  It also implements new Mac OS X 10.5 methods.

 
Review request changed
Updated 1 year, 1 month ago (July 28th, 2009, 3 p.m.)
  • This is a very intrusive NSSound patch and should be review very carefully.  Almost all of NSSound was touched and this new implementation is no longer compatible.  It also implements new Mac OS X 10.5 methods.

    This is a very intrusive NSSound patch and should be reviewed carefully.  Almost all of NSSound was touched and this new implementation is no longer compatible.  It also implements new Mac OS X 10.5 methods.
Posted 1 year, 1 month ago (July 28th, 2009, 4:58 p.m.)

   

  
  1. Overall the code is good, but there are a couple of memory bugs and your threading code is horrible.  You need to completely revisit pretty much anything that uses any of the locking classes - they are either used incorrectly or in the wrong place in a significant number of the places where they are used.  I didn't add comments on all of them, because a redesign of your approach to synchronisation is needed, rather than just a couple of quick bug fixes.  I didn't bother doing a full model of the concurrency in the code, but a number of race conditions appeared just glancing over the code.  
Why are these void* not id?  And why are they in a private array, while the other ivars have names?
  1. No reason, I guess using id is a lot better, I just wasn't thinking.  This also goes back to the ABI discussion we were having, but a void * array and id's really do not make that much of a difference.
  2. There is no real difference between an array of id (or void*) and a set of sensibly-named ivars.  The other alternative was to declare a single void*, which is a pointer to a structure containing the real ivars, which you malloc() in -init and free() in -dealloc.
Do you need to free _dev? 
  1. Yes, I'm just having some problems when I do it.  I'm still working through those.
  2. Nevermind that comment, I realized I had already worked that problem out... _dev is freed with the call to ao_close() in -close.
  3. What happens if the object is -release'd without being -close'd?
  4. In practice, the same thing that would happen if you do a ao_shutdown() without a corresponding ao_close().  The audio interface must be closed before being dealloced.  Should I be taking care this doesn't happen?
Is this meant to be a singleton?  If so, you should implement a +sharedInstance method and (optionally) override +alloc to make sure that only one is ever created.
  1. The thing here is that I can't just have a single instance of this class... lets say you try to play a 16 bit audio and then a 32 bit audio, at the same time?
  2. On OS X, you can only play one sound at a time using NSSound, so just changing the sound output mode would be fine.
  3. But can't you still have multiple NSSound instances playing at the same time?  In this code, each NSSound instance has an instance of a GSSoundSink and GSSoundSource.
Test for failure.  See the Étoilé SUPERINIT macro.
-playBytes:length: would be a more explanatory name for this method.
Remove extraneous space between function name and open bracket.
Creating this as a variable, rather than using NSMakeRange() directly makes the code less readable here (it looks like you will be using the range somewhere else).
Can you put this in a loadable bundle?  Since we have equivalent but more general functionality in Étoilé's MediaKit, we'll probably want to just use that, so we get support for pretty much everything via libavformat.
  1. It's already a loadable bundle.  As a matter of fact I based the GSSoundSink and GSSoundSource protocols on the MediaKit methods.  Both SndfileSource and AudioOutputSink are in the Bundles/ directory and have .nssound extensions (see -initWithData:).
  2. I'm sorry, I stated that incorrectly.  The SndfileSource and AudioOutputSink source files are in the Sound/ (new) directory.
Test for failure.  This is non-idiomatic, breaking the designated initialiser pattern.  If -initWithData: is the designated initialiser then -init should call it and it should call [super init], not [self init].  
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
You probably want to be using NSCondition here.  It should be committed soon, when Gregory finally finishes reviewing my code...
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
Don't explicitly give a size when initialising arrays unless you have good reason to.  Your sizes look quite random.
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
Uh, why are you copying the arrays here, and if there's a good reason for it why are you copying them like this rather than with -copy?
  1. That's how I understood the documents.  What I'm trying to do here is have a NSArray as opposed to a NSMutableArray (not sure if there's any difference, but that's what I was looking for).
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
This repeated locking and unlocking is far from ideal.  Consider copying the lockless ring buffer from MediaKit.  Or wait; there's an improved version waiting to be committed until Gregory commits my NSCondition patch...
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
If you really want to use ivars like this (which is ugly and provides no benefit), consider adding macros like this:

#define readLock ((NSConditionLock*)_private[2])
  1. I'll just change those to "id" instead of void *.  Thanks for that tip.
  2. Please use explicit class names rather than id where possible; it lets the compiler check that it really should respond to the messages you send it more easily.
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
This test is redundant.  Messages sent to nil are silently ignored.  This test is duplicated in objc_msg_lookup().
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
Do you need to test whether the delegate responds to that selector?
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
This RELEASE() looks unbalanced.  Where is the corresponding -retain that it balances?  If it is in the other thread (I think it is?) where is the locking to ensure that these happen in the right order?
  1. It's misplaced.  It should be in -_stream.  I do a retain right before I the call to performSelectorOnMainThread:withObject:.
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
This will crash in multithreaded code where you set the name of one sound while freeing another.  You need a lock around nameDict.
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
If this line is ever reached, you have a double-release bug.  self is retained when it is added to nameDict.  If it is dealloc'd while an object has retained it, then there is a serious problem.
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
Missing call to [super init];
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
Cast to void* is never needed.  Any object can be implicitly cast to void*.  See your copy of the C standard.
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
Send the message to isa instead of NSSound; it's faster and it plays nicer with subclasses.
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
Pasteboard types are horrible.  There are lots of different names with the same meaning.  You probably need to test for a few of them.
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
I'm fairly sure that's not how you are meant to use NSConditionLock...
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
I'm fairly sure -tryLock is not what you mean here either. 
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
You need to retain then autorelease this sound.  Otherwise it can become invalid if you change its name (in another thread, or in this one).
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
Lock nameDict.
/libs/gui/trunk/Source/NSSound.m (Diff revision 1)
 
Not needed.  self must always be retained by the caller.  If this is causing self to be released in other bits of your code, then you have bugs elsewhere.  Please fix them, rather than adding hacks that leak memory to prevent crashes.
Review request changed
Updated 1 year, 1 month ago (July 29th, 2009, 6:58 p.m.)
I went ahead and made some modifications that address some of the issues you brought up.  Others, like the thread thing, will take longer and I'll need to do some more research into it.  In the meanwhile, this is exactly what I have in my local copy.
Posted 1 year, 1 month ago (July 30th, 2009, 4:33 a.m.)

   

  
Really pedantic; 'plays with the device' rather than 'plays to the device'