[ 
https://issues.apache.org/jira/browse/THRIFT-1264?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Jan Rüth updated THRIFT-1264:
-----------------------------

    Description: 
When using TSocketClient in a Cocoa client and correctly managing the memory:
{code:title=Server.m|borderStyle=solid}
- (id)initWithServer:(NSString*)server port:(NSInteger)port
{
    self = [super init];
    if (self) {
        TSocketClient *transport = [[TSocketClient alloc] 
initWithHostname:server 
                                                       port:port];
        TBinaryProtocol *protocol = [[TBinaryProtocolFactory sharedFactory] 
newProtocolOnTransport:transport];
        Client *client = [[Client alloc] initWithProtocol:protocol];

// self.client is a retained property
        self.client = client;
        [client release];
        [protocol release];
        [transport release];
    }
    
    return self;
}


- (void)dealloc {
    self.client = nil;
    [super dealloc];
}
{code}
(maybe this should be fixed in the cocoa example, memory management is 
erroneous there)

After successfully releasing the object with the code above. One gets a 
EXC_BAD_ACCESS signal because the runloop send a responseToSelector: message to 
our object. With the selector stream:handleEvent:

This all leads to TSocketClient which opens two NSStream objects and delegates 
it self to both, afterwards both Streams are scheduled in the runloop, which 
should actually notify the delegate when something happens to the stream like 
data is available, this obviously results in the runloop asking TSocketClient 
for previously described selector. However this mechanism is not used, the 
TNSStreamTransport class is subclassed and responsible for read/write.

So why are the Streams schedules in the run loop in the first place?
Why are the streams not closed and released when the object gets deallocated?

I could fix this behavior by implementing a dealloc method in TSocketClient and 
making the Streams member variables
{code:title=TSocketClient.h|borderStyle=solid}
@interface TSocketClient : TNSStreamTransport {
    NSInputStream * inputStream;
        NSOutputStream * outputStream;
}
{code}

{code:title=TSocketClient.m|borderStyle=solid}
-(void)dealloc {
    [inputStream close];
    [outputStream close];
    [inputStream release];
    [outputStream release];
    [super dealloc];
}
{code}
However I still don't know if that is the right way.
Suggestions?


Edit:

The bug is still present in 0.9 I attached a TSocketClient.m that fixes this 
Problem, the TSocketClient also keeps track of the streams and upon 
deallocation it closes the stream, removes it from the runloop and releases it 
(if arc is not enabled). This also removes a bug in non-arc environment that 
where the streams have a retaincount of +1 after deallocation such that the bug 
described here won't occur and the streams leak memory


  was:
When using TSocketClient in a Cocoa client and correctly managing the memory:
{code:title=Server.m|borderStyle=solid}
- (id)initWithServer:(NSString*)server port:(NSInteger)port
{
    self = [super init];
    if (self) {
        TSocketClient *transport = [[TSocketClient alloc] 
initWithHostname:server 
                                                       port:port];
        TBinaryProtocol *protocol = [[TBinaryProtocolFactory sharedFactory] 
newProtocolOnTransport:transport];
        Client *client = [[Client alloc] initWithProtocol:protocol];

// self.client is a retained property
        self.client = client;
        [client release];
        [protocol release];
        [transport release];
    }
    
    return self;
}


- (void)dealloc {
    self.client = nil;
    [super dealloc];
}
{code}
(maybe this should be fixed in the cocoa example, memory management is 
erroneous there)

After successfully releasing the object with the code above. One gets a 
EXC_BAD_ACCESS signal because the runloop send a responseToSelector: message to 
our object. With the selector stream:handleEvent:

This all leads to TSocketClient which opens two NSStream objects and delegates 
it self to both, afterwards both Streams are scheduled in the runloop, which 
should actually notify the delegate when something happens to the stream like 
data is available, this obviously results in the runloop asking TSocketClient 
for previously described selector. However this mechanism is not used, the 
TNSStreamTransport class is subclassed and responsible for read/write.

So why are the Streams schedules in the run loop in the first place?
Why are the streams not closed and released when the object gets deallocated?

I could fix this behavior by implementing a dealloc method in TSocketClient and 
making the Streams member variables
{code:title=TSocketClient.h|borderStyle=solid}
@interface TSocketClient : TNSStreamTransport {
    NSInputStream * inputStream;
        NSOutputStream * outputStream;
}
{code}

{code:title=TSocketClient.m|borderStyle=solid}
-(void)dealloc {
    [inputStream close];
    [outputStream close];
    [inputStream release];
    [outputStream release];
    [super dealloc];
}
{code}
However I still don't know if that is the right way.
Suggestions?



    
> TSocketClient is queried by run loop after deallocation in Cocoa
> ----------------------------------------------------------------
>
>                 Key: THRIFT-1264
>                 URL: https://issues.apache.org/jira/browse/THRIFT-1264
>             Project: Thrift
>          Issue Type: Bug
>          Components: Cocoa - Library
>    Affects Versions: 0.7, 0.9
>         Environment: iPad, iOS SDK 4.3, iOS SDK 5.1
>            Reporter: Jan Rüth
>            Priority: Critical
>             Fix For: 0.9
>
>         Attachments: THRIFT-1264.diff, TSocketClient.m
>
>
> When using TSocketClient in a Cocoa client and correctly managing the memory:
> {code:title=Server.m|borderStyle=solid}
> - (id)initWithServer:(NSString*)server port:(NSInteger)port
> {
>     self = [super init];
>     if (self) {
>         TSocketClient *transport = [[TSocketClient alloc] 
> initWithHostname:server 
>                                                        port:port];
>         TBinaryProtocol *protocol = [[TBinaryProtocolFactory sharedFactory] 
> newProtocolOnTransport:transport];
>         Client *client = [[Client alloc] initWithProtocol:protocol];
> // self.client is a retained property
>         self.client = client;
>         [client release];
>         [protocol release];
>         [transport release];
>     }
>     
>     return self;
> }
> - (void)dealloc {
>     self.client = nil;
>     [super dealloc];
> }
> {code}
> (maybe this should be fixed in the cocoa example, memory management is 
> erroneous there)
> After successfully releasing the object with the code above. One gets a 
> EXC_BAD_ACCESS signal because the runloop send a responseToSelector: message 
> to our object. With the selector stream:handleEvent:
> This all leads to TSocketClient which opens two NSStream objects and 
> delegates it self to both, afterwards both Streams are scheduled in the 
> runloop, which should actually notify the delegate when something happens to 
> the stream like data is available, this obviously results in the runloop 
> asking TSocketClient for previously described selector. However this 
> mechanism is not used, the TNSStreamTransport class is subclassed and 
> responsible for read/write.
> So why are the Streams schedules in the run loop in the first place?
> Why are the streams not closed and released when the object gets deallocated?
> I could fix this behavior by implementing a dealloc method in TSocketClient 
> and making the Streams member variables
> {code:title=TSocketClient.h|borderStyle=solid}
> @interface TSocketClient : TNSStreamTransport {
>     NSInputStream * inputStream;
>       NSOutputStream * outputStream;
> }
> {code}
> {code:title=TSocketClient.m|borderStyle=solid}
> -(void)dealloc {
>     [inputStream close];
>     [outputStream close];
>     [inputStream release];
>     [outputStream release];
>     [super dealloc];
> }
> {code}
> However I still don't know if that is the right way.
> Suggestions?
> Edit:
> The bug is still present in 0.9 I attached a TSocketClient.m that fixes this 
> Problem, the TSocketClient also keeps track of the streams and upon 
> deallocation it closes the stream, removes it from the runloop and releases 
> it (if arc is not enabled). This also removes a bug in non-arc environment 
> that where the streams have a retaincount of +1 after deallocation such that 
> the bug described here won't occur and the streams leak memory

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira

Reply via email to