BLIP/BLIPWriter.m
author Jens Alfke <jens@mooseyard.com>
Fri May 30 13:54:38 2008 -0700 (2008-05-30)
changeset 9 980beba83fb7
parent 0 9d67172bb323
child 11 29e8b03c05d4
permissions -rw-r--r--
Fixed a serious bug - a race condition where a data buffer in the writer's queue could be dealloced (not the NSData, but its bytes themselves) before the writer sent it, resulting in an EFAULT error.
jens@0
     1
//
jens@0
     2
//  BLIPFrameWriter.m
jens@0
     3
//  MYNetwork
jens@0
     4
//
jens@0
     5
//  Created by Jens Alfke on 5/18/08.
jens@0
     6
//  Copyright 2008 Jens Alfke. All rights reserved.
jens@0
     7
//
jens@0
     8
jens@0
     9
#import "BLIPReader.h"
jens@0
    10
#import "BLIPWriter.h"
jens@0
    11
#import "BLIP_Internal.h"
jens@0
    12
jens@1
    13
#import "Logging.h"
jens@1
    14
#import "Test.h"
jens@1
    15
jens@0
    16
jens@0
    17
#define kDefaultFrameSize 4096
jens@0
    18
jens@0
    19
jens@0
    20
@implementation BLIPWriter
jens@0
    21
jens@0
    22
jens@0
    23
- (void) dealloc
jens@0
    24
{
jens@0
    25
    [_outBox release];
jens@0
    26
    [super dealloc];
jens@0
    27
}
jens@0
    28
jens@0
    29
- (void) disconnect
jens@0
    30
{
jens@0
    31
    [_outBox makeObjectsPerformSelector: @selector(_connectionClosed) withObject: nil];
jens@0
    32
    setObj(&_outBox,nil);
jens@0
    33
    [super disconnect];
jens@0
    34
}
jens@0
    35
jens@0
    36
@synthesize numQueriesSent=_numQueriesSent;
jens@0
    37
jens@0
    38
jens@0
    39
- (BOOL) isBusy
jens@0
    40
{
jens@0
    41
    return _outBox.count>0 || [super isBusy];
jens@0
    42
}
jens@0
    43
jens@0
    44
jens@0
    45
- (void) _queueMessage: (BLIPMessage*)msg isNew: (BOOL)isNew
jens@0
    46
{
jens@0
    47
    int n = _outBox.count, index;
jens@0
    48
    if( msg.urgent && n > 1 ) {
jens@0
    49
        // High-priority gets queued after the last existing high-priority message,
jens@0
    50
        // leaving one regular-priority message in between if possible.
jens@0
    51
        for( index=n-1; index>0; index-- ) {
jens@0
    52
            BLIPMessage *otherMsg = [_outBox objectAtIndex: index];
jens@0
    53
            if( [otherMsg urgent] ) {
jens@0
    54
                index = MIN(index+2, n);
jens@0
    55
                break;
jens@0
    56
            } else if( isNew && otherMsg._bytesWritten==0 ) {
jens@0
    57
                // But have to keep message starts in order
jens@0
    58
                index = index+1;
jens@0
    59
                break;
jens@0
    60
            }
jens@0
    61
        }
jens@0
    62
        if( index==0 )
jens@0
    63
            index = 1;
jens@0
    64
    } else {
jens@0
    65
        // Regular priority goes at the end of the queue:
jens@0
    66
        index = n;
jens@0
    67
    }
jens@0
    68
    if( ! _outBox )
jens@0
    69
        _outBox = [[NSMutableArray alloc] init];
jens@0
    70
    [_outBox insertObject: msg atIndex: index];
jens@0
    71
    
jens@0
    72
    if( isNew ) {
jens@0
    73
        LogTo(BLIP,@"%@ queuing outgoing %@ at index %i",self,msg,index);
jens@0
    74
        if( n==0 )
jens@0
    75
            [self queueIsEmpty];
jens@0
    76
    }
jens@0
    77
}
jens@0
    78
jens@0
    79
jens@0
    80
- (BOOL) sendMessage: (BLIPMessage*)message
jens@0
    81
{
jens@0
    82
    if( _shouldClose ) {
jens@0
    83
        Warn(@"%@: Attempt to send a message after the connection has started closing",self);
jens@0
    84
        return NO;
jens@0
    85
    }
jens@0
    86
    Assert(!message.sent,@"message has already been sent");
jens@0
    87
    [self _queueMessage: message isNew: YES];
jens@0
    88
    return YES;
jens@0
    89
}
jens@0
    90
jens@0
    91
jens@0
    92
- (BOOL) sendRequest: (BLIPRequest*)q response: (BLIPResponse*)response
jens@0
    93
{
jens@0
    94
    if( !_shouldClose ) {
jens@0
    95
        [q _assignedNumber: ++_numQueriesSent];
jens@0
    96
        if( response ) {
jens@0
    97
            [response _assignedNumber: _numQueriesSent];
jens@0
    98
            [(BLIPReader*)self.reader _addPendingResponse: response];
jens@0
    99
        }
jens@0
   100
    }
jens@0
   101
    return [self sendMessage: q];
jens@0
   102
}
jens@0
   103
jens@0
   104
jens@0
   105
- (void) queueIsEmpty
jens@0
   106
{
jens@0
   107
    if( _outBox.count > 0 ) {
jens@0
   108
        // Pop first message in queue:
jens@0
   109
        BLIPMessage *msg = [[_outBox objectAtIndex: 0] retain];
jens@0
   110
        [_outBox removeObjectAtIndex: 0];
jens@0
   111
        
jens@0
   112
        // As an optimization, allow message to send a big frame unless there's a higher-priority
jens@0
   113
        // message right behind it:
jens@0
   114
        size_t frameSize = kDefaultFrameSize;
jens@0
   115
        if( msg.urgent || _outBox.count==0 || ! [[_outBox objectAtIndex: 0] urgent] )
jens@0
   116
            frameSize *= 4;
jens@0
   117
        
jens@0
   118
        if( [msg _writeFrameTo: self maxSize: frameSize] ) {
jens@0
   119
            // add it back so it can send its next frame later:
jens@0
   120
            [self _queueMessage: msg isNew: NO];
jens@0
   121
        }
jens@0
   122
        [msg release];
jens@0
   123
    } else {
jens@0
   124
        LogTo(BLIPVerbose,@"%@: no more work for writer",self);
jens@0
   125
    }
jens@0
   126
}
jens@0
   127
jens@0
   128
jens@0
   129
jens@0
   130
@end
jens@0
   131
jens@0
   132
jens@0
   133
/*
jens@0
   134
 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
jens@0
   135
 
jens@0
   136
 Redistribution and use in source and binary forms, with or without modification, are permitted
jens@0
   137
 provided that the following conditions are met:
jens@0
   138
 
jens@0
   139
 * Redistributions of source code must retain the above copyright notice, this list of conditions
jens@0
   140
 and the following disclaimer.
jens@0
   141
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
jens@0
   142
 and the following disclaimer in the documentation and/or other materials provided with the
jens@0
   143
 distribution.
jens@0
   144
 
jens@0
   145
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
jens@0
   146
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
jens@0
   147
 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
jens@0
   148
 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
jens@0
   149
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
jens@0
   150
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
jens@0
   151
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
jens@0
   152
 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
jens@0
   153
 */