weird SIGSEGV crashes

Help
matt
2002-06-22
2002-06-24
  • matt

    matt - 2002-06-22

    i have a tableview wired to the file's owner class that calls the connectionsTableViewClicked method (single click, not double click). i also have some text fields whose values should be populated depending on which row in the table view was clicked. whenever a row in the table is clicked, the program crashes.

    i altered the data source class from the data access example. instead of using a blessed hashref and storing an array full of table rows for a tableview data source in a hash key, i just made the class a blessed array ref. pretty much everything else, with the exception of a few changes, is the same in the data source class.

    my explanation of the problem seems incomplete, so let me know if there's any other info you need

    thanks,
    matt

    ___________________

    #
    #  Moxic
    #  MoxicDataSource.pm
    #

    package MoxicDataSource;

    use Foundation;
    use Foundation::Functions;
    use AppKit;
    use AppKit::Functions;

    @ISA = qw(Exporter);

    # Declare Objective-C methods and signatures for this package

    $OBJC_EXPORT{'numberOfRowsInTableView:'} =
        {
            'method' => 'numberOfRowsInTableView',
            'return' => 'i',
        };

    $OBJC_EXPORT{'tableView:objectValueForTableColumn:row:'} =
        {
            'method' => 'tableView_objectValueForTableColumn_row',
            'args' => '@@i',
        };

    $OBJC_EXPORT{'tableView:setObjectValue:forTableColumn:row:'} =
        {
        'method' => 'tableView_setObjectValue_forTableColumn_row',
        'args' => '@@@i',
        'return' => 'v',
        };

    sub new {
        # Typical Perl constructor
        # See 'perltoot' for details
        my $proto = shift;
        my $class = ref($proto) || $proto;
        my $self = [];

        bless ($self, $class);

        return $self;
    }

    # Methods for conforming to NSTableDataSource protocol

    sub numberOfRowsInTableView {
        my ($self, $tableView) = @_;
        return scalar @{$self};
    }

    sub tableView_objectValueForTableColumn_row {
        my ($self, $tableView, $tableColumn, $row) = @_;
        return $self->[$row]->{$tableColumn->identifier};
    }

    sub tableView_setObjectValue_forTableColumn_row {
        my ($self, $tableView, $objectValue, $tableColumn, $row) = @_;
        $self->[$row]->{$tableColumn->identifier} = $objectValue;
    }

    1;

    #
    #  Moxic
    #  MoxicWindowController.pm
    #

    package MoxicWindowController;

    use Foundation;
    use Foundation::Functions;
    use AppKit;
    use AppKit::Functions;

    use MoxicDataSource;

    @ISA = qw(Exporter);

    sub new {
        # Typical Perl constructor
        # See 'perltoot' for details
        my $proto = shift;
        my $class = ref($proto) || $proto;
        my $self = {};

        # Outlets
        # Main Window
        $self->{'MainWindow'} = undef;
        $self->{'MainTabView'} = undef;
        $self->{'MainTextView'} = undef;
        $self->{'MainTextField'} = undef;
        $self->{'MainSendButton'} = undef;

        # Preferences Window
        $self->{'PreferencesWindow'} = undef;
        $self->{'PreferencesTabView'} = undef;
        $self->{'PreferencesConnectionsTableView'} = undef;
        $self->{'PreferencesNickField'} = undef;
        $self->{'PreferencesUsernameField'} = undef;
        $self->{'PreferencesRealNameField'} = undef;
        $self->{'PreferencesServerField'} = undef;
        $self->{'PreferencesPortField'} = undef;
        $self->{'PreferencesDisplayMOTDCheckBox'} = undef;
        $self->{'PreferencesHidePingRequestsCheckBox'} = undef;

        bless ($self, $class);

        $self->{'NSWindowController'} =
            NSWindowController->alloc->initWithWindowNibName_owner("MainWindow", $self);
        $self->{'NSWindowController'}->window;

        $self->{'MainTextView'}->setString("Welcome to Moxic version 0.1!\n");

        # Set up data sources

        my $dataSource = MoxicDataSource->new;
        push @{$dataSource}, { Connections => "Untitled", PreferencesNickField => "mattfelsen" };
        push @{$dataSource}, { Connections => "Untitled2", PreferencesNickField => "mattfelsen2" };
        $self->{'PreferencesConnectionsTableView'}->setDataSource($dataSource);

        return $self;
    }

    # Actions for the main window

    sub sendButtonClicked {
        my ($self, $sender) = @_;
        $self->parseTextFieldInput($self->{'MainTextField'});
    }

    sub parseTextFieldInput {
        my ($self, $sender) = @_;

        if ($sender->stringValue =~ m!^/!) {
            # it's a command (change this to check against command character
            # stored in preferences (must implement prefs, too, heh)
        }

        $sender->setStringValue("");
    }

    # Actions for the preferences window

    # this is where it crashes

    sub connectionsTableViewClicked {
        my ($self, $tableView) = @_;

        my $dataSource = $tableView->dataSource;
        my $row = $tableView->selectedRow;

        foreach my $key (keys %{$dataSource->[$row]}) {
            $self->{$key}->setStringValue($dataSource->[$row]->{$key}) unless $key eq "Connections";
        }
    }

    sub addConnectionClicked {
        my ($self, $sender) = @_;

        my $tableView = $self->{'PreferencesConnectionsTableView'};
        my $dataSource = $tableView->dataSource;

        push @{$dataSource}, { Connections => "Untitled" };
        $tableView->reloadData;

    }

    sub removeConnectionClicked {
        my ($self, $sender) = @_;
    }

    sub displayMOTDClicked {
        my ($self, $sender) = @_;
    }

    sub hidePingRequestsClicked {
        my ($self, $sender) = @_;
    }

    1;

    And here's the crash log:

    Date/Time:  2002-06-22 19:50:47 -0400
    OS Version: 10.1.5 (Build 5S66)
    Host:       localhost

    Command:    Moxic
    PID:        956

    Exception:  EXC_BAD_ACCESS (0x0001)
    Codes:      KERN_INVALID_ADDRESS (0x0001) at 0x7ffffea9

    Thread 0 Crashed:
    #0   0x7198cb44 in Perl_hv_exists
    #1   0x10003008 in -[CBPerlObject hasProperty:]
    #2   0x10003460 in -[CBPerlObject respondsToSelector:]
    #3   0x70c4b3d8 in -[NSTableView _dataSourceRespondsToWriteDragRows]
    #4   0x70c6d06c in -[NSTableView _shouldDelayOrderingForDragAttempt]
    #5   0x70c6d038 in -[NSTableView shouldDelayWindowOrderingForEvent:]
    #6   0x70c1ad6c in -[NSWindow sendEvent:]
    #7   0x70b9479c in -[NSApplication sendEvent:]
    #8   0x70c23488 in -[NSApplication run]
    #9   0x706bb378 in objc_msgSendv
    #10  0x70811c2c in -[NSInvocation invoke]
    #11  0x10006f9c in CBCallNativeMethod
    #12  0x1000571c in XS_Foundation__callObjectMethod
    #13  0x719978d0 in Perl_pp_entersub
    #14  0x71990694 in Perl_runops_standard
    #15  0x7194411c in S_call_body
    #16  0x719442ec in perl_eval_sv
    #17  0x71944584 in perl_eval_pv
    #18  0x10001d9c in -[CBPerl eval:]
    #19  0x10001960 in CBApplicationMain2
    #20  0x10001804 in CBApplicationMain
    #21  0x00001eb4 in main
    #22  0x00001dd8 in _start
    #23  0x00001c08 in start

    Thread 1:
    #0   0x7003f4c8 in semaphore_wait_signal_trap
    #1   0x7003f2c8 in _pthread_cond_wait
    #2   0x7086c34c in -[NSConditionLock lockWhenCondition:]
    #3   0x70ba1358 in -[NSUIHeartBeat _heartBeatThread:]
    #4   0x70842358 in forkThreadForFunction
    #5   0x7002054c in _pthread_body

    Thread 2:
    #0   0x70000978 in mach_msg_overwrite_trap
    #1   0x70005a04 in mach_msg
    #2   0x70026a2c in _pthread_become_available
    #3   0x70026724 in pthread_exit
    #4   0x70020550 in _pthread_body

    PPC Thread State:
      srr0: 0x7198cb44 srr1: 0x0000f030                vrsave: 0x00000000
       xer: 0x00000020   lr: 0x7198c9ac  ctr: 0x7198c998   mq: 0x00000000
        r0: 0x028d6908   r1: 0xbfffe6e0   r2: 0x021a582a   r3: 0x7ffffea9
        r4: 0x021a5809   r5: 0x00000021   r6: 0x01871b00   r7: 0x51ad210f
        r8: 0x021a582a   r9: 0x0000000c  r10: 0xffffffff  r11: 0x51ad20d5
       r12: 0x7198c998  r13: 0x00000000  r14: 0x00000000  r15: 0x00000000
       r16: 0x00000000  r17: 0x00000000  r18: 0x80b90fc4  r19: 0x00000000
       r20: 0x00000000  r21: 0x00000000  r22: 0x00000000  r23: 0x706c0928
       r24: 0x021b3c50  r25: 0x01874970  r26: 0x80b90f1c  r27: 0x021a5809
       r28: 0x00000021  r29: 0x543a8a17  r30: 0x81948ae0  r31: 0x7198c9ac

    **********

     
    • matt

      matt - 2002-06-23

      update

      it also crashes on the addConnectionClicked method. could the problem be that the datasource was a lexical variable when created and went out of scope at the end of the MoxicWindowController constructor, so that when i called $tableView->dataSource it was returning a reference to a variable that no longer exists?

       
    • Sherm Pendley

      Sherm Pendley - 2002-06-23

      Fair warning, you're venturing into unexplored territory - I've only tested CB with blessed hashes, never with blessed arrays. Here there be dragons! :-)

      If I follow you correctly, data is being displayed OK in your table, it's not until you click on a row that the crash happens. Is that right? If so, I think that something's odd with the reference you're getting back when you call $tableView->dataSource.

      It doesn't look as if you're getting back a reference to the same Perl object you passed to setDataSource. Instead, what you're getting back is a bridged reference to an Objective-C object that's "wrapped" around the Perl object. A wrapper around a wrapper, in other words.

      I'll have to write a more isolated test case to verify this, but I think that when you execute $dataSource->[$row] on the wrapper object, that's being translated to a method call, and it's that method call that's eventually failing. First, the wrapper class tries to find an explicitly exported method using the OBJC_EXPORT hash, then it looks for a "real" Perl method, and if it fails at that, it looks for an instance variable to see if the method being called should be treated as an accessor method. This last step is where the Perl_hv_exists is being called - there's no check in place to verify that the object being referred to is a hash, before the check for the instance variable is done.

      In the longer term, I'll be running some tests, to figure out a) what's happening to $dataSource during the round trip, and b) what method is being called when $dataSource->[$row] is evaluated by Perl. I'll also add an explicit check to CBPerlObject's hasProperty: method, so that it always returns false for objects that are implemented as arrays rather than hashes.

      In the short term, there's a pretty easy workaround. In the constructor for MoxicWindowController, you can simply create an instance variable to hold $dataStore, with $self->{'ds'} = $dataStore for example, and in your action methods, use $self->{'ds'} instead of $tableView->dataSource to retrieve it.

       
    • matt

      matt - 2002-06-23

      yes. the table displays the data correctly and the app crashes once a row is clicked.

      i changed the MoxicDataSource class to use a blessed hash and store the array in $self->{'data'}. in the MoxicWindowController, after setting the table view's data source to $dataSource, i then stored $dataSouce in $self->{'ds'}. (so i'm using $self->{'ds'}->{'data'} instead of $dataSource, blech :P). i'm happy to say that it works this way (although i'm unhappy with this workaround). i was hoping that each table would be able to keep track of its own data source and each was a difference instance of MoxicDataSource, so that i wouldn't have to worry about keeping track and organizing the data sources in $self, i could just do $tableView->dataSource (i'll be using more than one table view, so you can see why i don't want to store everything in $self)

      in the connectionsTableViewClicked method, i logged some things: $tableView (from @_), $self->{'PreferencesConnectionsTableView'}, $tableView->dataSource, and $self->{'ds'}. here's what happened:

      $tableView produced a new memory location each time (after clickign repeatedely..i wasn't closing and reopening the prefs window), $self->{'PreferencesConnectionsTableView'} logged the same mem location each time, and (which surprised me) $tableView->dataSource and $self->{'ds'} both logged the same memory location; however, the app crashed after NSLog($tableView->dataSource) and i have no clue why.

      thanks for the workaround,
      matt

       
    • matt

      matt - 2002-06-23

      hrm...since it doesn't work with blessed array refs, i'm assuming pseudo-hashes wouldn't work either :P

       
    • Sherm Pendley

      Sherm Pendley - 2002-06-23

      I've made some progress... There are actually two separate issues involved here. One was easy to fix, and the other is giving me a headache.

      The first is that an attempt to call a non-existent method on a Perl object would coredump, unless the object was a hash. When a method is called that doesn't exist, CB tries to auto-generate an accessor method, by looking to see if a matching instance variable exists. I've added code to the CVS version of CBPerlObject.m to verify that the object is a hash before doing that check.

      The other problem is more elusive, and appears to be a reentrancy issue. If you pass a Perl object to an Objective-C object, and then within an action method you call a method that should return an "unwrapped" reference to the original Perl object, the result is a crash. What's puzzling about it is that the stack trace indicates that Objective-C is trying to call a Perl method, not the other way around.

      I've been able to duplicate this behavior, regardless of whether the object in question is a hash or an array, using any of the following:

      NSLog(ref $sender->dataSource);
      NSLog(ref $sender->target);
      NSLog(ref $self->{'NSWindowController'}->owner);

       
    • matt

      matt - 2002-06-23

      thanks for the quick responses. is there a way you can allow downloads of a current build from cvs, or set up a nightly build script?

       
      • Sherm Pendley

        Sherm Pendley - 2002-06-24

        I love the idea in principle, the problem is that SourceForge makes it impossibly painful to put into practice. The "file download" system here is incredibly klunky; using it every day to make a new build available would be about as pleasant as a daily visit to the dentist.

         

Log in to post a comment.

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:

JavaScript is required for this form.





No, thanks