区分一个子调用与另一个子调用

时间:2013-08-28 17:41:52

标签: multithreading perl

在下面的片段中,如何区分子foo的第二个调用实例与第一个调用实例?

while ($whatever) {
  foo(); foo();     # foo() and foo() have the same caller package, file, and line
}

像返回文件的超级caller()之类的东西可以解决问题。我不想使用源过滤器。

背景,或者,这不是一个XY问题吗?

我有一个便利模块,Local :: Thread :: Once,以OO-ish方式公开像pthread_once / std::call_once这样的功能,也作为子例程属性。这些都很容易,因为在任何一种情况下都有一个自然而明确的“once_control”或“once_flag”。

但是,还有一个程序界面 - once { ... } - 目前根据caller返回的$filename$line进行序列化。像这样:

sub once(&) {
  my $user_routine = shift;
  my (undef, $file, $line) = caller;

  my $once_control = get_a_shared_flag_just_for_this_invocation($file, $line);

  lock($once_control);
  if (! $once_control) { $once_control++; $user_routine->(); }
  return;
}

这不正是它的工作原理 - 真正的一个更有效 - 但重点是,调用是从调用者的文件和行键入的。这是有效的,除了它无法区分同一行上的两个调用。

while ($whatever) {
  once { foo(); }
  once { bar(); }                    # OK, foo() and bar() each called only once
  once { baz(); }; once { buz(); };  # :(  buz() not called, not even once
}

请注意,$user_routine的地址不能用作附加判别式,因为subs从一个ithread复制到另一个。

我可以将这个问题视为一个非常人为的用例的文档限制,但我更愿意以某种方式修复它。

3 个答案:

答案 0 :(得分:6)

Devel::Callsite正是为了这个目的写的。

答案 1 :(得分:3)

在我明白你在说什么之前,我必须阅读几次。 “超级来电”功能怎么样:

my @last_caller = ("","","",0);
sub super_caller {
    my ($pkg,$file,$line) = caller(1 + shift);
    if ($pkg eq $last_caller[0] &&
        $file eq $last_caller[1] &&
        $line eq $last_caller[2]) {
        $last_caller[3]++;
    } else {
        @last_caller = ($pkg,$file,$line,1);
    }
    return @last_caller;
}

就像caller,但第4个元素是我们连续多少次看到这个确切的包,文件和行的计数。

答案 2 :(得分:1)

对我来说,光谱仍然是一种黑魔法,但这是我的观察:

  1. 在行走代码参考的选项时,您会遇到一个B::COP结构
  2. B::COP结构包含filelinecop_seq属性(以及其他)
  3. 不同子例程定义的cop_seq属性不同
  4. 这些都是正确的,而不是一个非常不完整的模型,你可以使用文件,行和cop_seq作为键,或者甚至只是cop_seq 。这是一个概念证明:

    use B;
    
    sub once (&) {
        my $code = shift;
        my $key = get_cop_seq($code);
        print "once called with code '$key'\n";
    }
    
    my $optreedata;
    sub get_cop_seq {
        my $code = shift;
        $optreedata = "";
        B::walkoptree( B::svref_2object($code)->ROOT, "find_cop_seq" );
        return $optreedata;
    }
    sub B::OP::find_cop_seq {
        my $op = shift;
        if (ref $op eq 'B::COP') {
            $optreedata .= sprintf "%s:%d:%d", $op->file, $op->line, $op->cop_seq;
        }
    }
    
    sub foo { 42 }
    sub bar { 19 };
    
    once { foo };                  # this is line 26
    once { bar };
    once { foo }; once { bar };
    once { bar } for 1..5;         # line 29
    

    这是输出(结果可能会有所不同):

    once called with code 'super-caller2.pl:26:205'
    once called with code 'super-caller2.pl:27:206'
    once called with code 'super-caller2.pl:28:207'  <--- two calls for line 28
    once called with code 'super-caller2.pl:28:208'    |- with different cop_seq
    once called with code 'super-caller2.pl:29:209'      
    once called with code 'super-caller2.pl:29:209'      
    once called with code 'super-caller2.pl:29:209'  <--- but 5 calls for line 29
    once called with code 'super-caller2.pl:29:209'       with the same cop_seq
    once called with code 'super-caller2.pl:29:209'