【问题标题】:Possibility for deadlock to occur during IPC call在 IPC 调用期间发生死锁的可能性
【发布时间】:2013-01-12 05:54:20
【问题描述】:

我有一个MainActivity,它将向远程AutoCompleteService 服务发出IPC 调用。

AutoCompleteService的IPC函数执行期间,服务将再次向MainActivity发出IPC调用。

MainActivity.java

// Receive IPC call from AutoCompleteService.
private StockInfoObserver.Stub stockInfoObserver = new StockInfoObserver.Stub() {

    @Override
    public void update(StockInfo stockInfo) throws RemoteException {
        // TODO Auto-generated method stub
        Log.i(TAG, android.os.Process.myPid() + " : MainActivity receive ipc call : " + Thread.currentThread().getId());
    }

};

...
...
...

// Issue IPC call to AutoCompleteService.
button.setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View arg0) {
        // Test on API.
        try {
            Log.i(TAG, android.os.Process.myPid() + " : MainActivity start issue IPC call to remote service : " + Thread.currentThread().getId());
            // autoCompleteApi.handle will issue IPC call to remote service.
            autoCompleteApi.handle("abc");
            Log.i(TAG, android.os.Process.myPid() + " : MainActivity end issue IPC call to remote service : " + Thread.currentThread().getId());
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

});

AutoCompleteService.java

private AutoCompleteApi.Stub autoCompleteApi = new AutoCompleteApi.Stub() {    
    private List<StockInfoObserver> stockInfoObservers = new ArrayList<StockInfoObserver>();

    @Override
    public void handle(String string) {
        Log.i(TAG, android.os.Process.myPid() + " : AutoCompleteService start receive ipc call : " + Thread.currentThread().getId());
        try {
            for (StockInfoObserver stockInfoObserver : stockInfoObservers) {    
                Log.i(TAG, android.os.Process.myPid() + " : AutoCompleteService start IPC call to MainActivity : " + Thread.currentThread().getId());
                // stockInfoObserver.update will issue IPC call back to MainActivity
                stockInfoObserver.update(null);
                Log.i(TAG, android.os.Process.myPid() + " : AutoCompleteService end IPC call to MainActivity : " + Thread.currentThread().getId());
            }
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Log.i(TAG, android.os.Process.myPid() + " : AutoCompleteService end receive ipc call : " + Thread.currentThread().getId());
    }

    @Override
    public void attachStockInfoObserver(StockInfoObserver stockInfoObserver)
            throws RemoteException {
        if (stockInfoObservers.contains(stockInfoObserver) == false) {
            stockInfoObservers.add(stockInfoObserver);
        }
    }
};

我最初的预期是,会发生死锁。这是由于我的观察。发出 IPC 调用时,发出者只会在 IPC 接收者完成其 IPC 函数执行后从 IPC 调用返回。

  1. MainActivity 通过autoCompleteApi.handleAutoCompleteService 发出IPC 调用。
  2. MainActivity 现在将等待 AutoCompleteService 完成执行。
  3. AutoCompleteService 通过stockInfoObserver.updateMainActivity 发出IPC 调用。
  4. AutoCompleteService 现在将等待 MainActivity 完成执行。
  5. 但是,MainActivity 的线程还在等待,没有线程可以执行update 函数。
  6. 两个进程一直相互等待。

但是,上述情况不会发生。这是我得到的日志。一切都完美无缺。

// Log in MainActivity TAG
3930 : MainActivity start issue IPC call to remote service : 1
3930 : MainActivity receive ipc call : 1
3930 : MainActivity end issue IPC call to remote service : 1

// Log in AutoCompleteService TAG
3961 : AutoCompleteService start receive ipc call : 494
3961 : AutoCompleteService start IPC call to MainActivity : 494
3961 : AutoCompleteService end IPC call to MainActivity : 494
3961 : AutoCompleteService end receive ipc call : 494

但我真的不明白。 如果 MainActivity 线程(Id 为 1)没有从函数调用 (autoCompleteApi.handle) 返回,它如何“跳转”到执行另一个函数 (update(StockInfo stockInfo))?

我希望 MainActivity 接收 ipc 调用 被不同的线程打印。不是 ID 为 1 的线程。如果不是,应该会发生死锁。

如果您有兴趣尝试,请在此处下载完整的源代码:https://www.dropbox.com/s/8hd7v5acjd213l1/jstock-android2.zip

【问题讨论】:

  • 我的猜测是 LogCat 无法跟上这里的流程变化。在写入来自 AutoCompleteService 的日志之前,您收到返回给 MainActivity 的 IPC 回调真的没有任何意义。
  • 对不起。日志来自 2 个不同的 TAG。

标签: android


【解决方案1】:

一个有趣的问题。我首先认为传入的 IPC 调用是在不同的线程上处理的(因为它通常会发生在传入的 IPC 调用中)在这种特定情况下结果是错误的。

查看传入调用到达时的执行堆栈(最近的堆栈帧在顶部):

MainActivity$1.update
MainActivity$1.onTransact
MainActivity$1.execTransact         <- this gets called by the incoming IPC call
BinderProxy.transact                <- this is where the outgoing IPC call is made
AutoCompleteApi$Stub$Proxy.handle
MainActivity$3.onClick
...

所以一切都发生在同一个线程中。呼入看起来像呼出的子程序调用。使这成为可能的魔法发生在本机代码中(或者可能在内核中)。有一个interesting paper by Thorsten Schreiber 解释了Binder 机制的一些内部结构。不幸的是,它没有讨论与本例中发生的相同进程的反向调用。

this blog post中有一个关于该功能的小注释:

请注意,原始线程在等待回复时也可能收到 BR_TRANSACTION 命令。这表示跨进程的递归,接收线程调用原始进程中的对象。驱动程序有责任跟踪所有活动事务,因此它可以在递归发生时将事务分派到正确的线程。

也许在OpenBinder website,Android Binder 实现的根目录中可以找到更多内容。

【讨论】:

  • 但是如果你查看我的日志,你会发现 MainActivity 中正在运行相同的线程。
  • @YanChengCHEOK:那么你误解了你的日志。例如,您正在记录进程 ID 并认为它们是线程 ID。
  • @CommonsWare:为什么这么说?我认为Thread.currentThread().getId 确实证明正在运行相同的线程?请注意,我同时记录了进程 ID(正面)和线程 ID(背面)
  • @YanChengCHEOK 说得好,我需要更仔细地调查一下。
  • @YanChengCHEOK:哎呀,抱歉——我关注的是左边的数字列,它们是您的进程 ID。话虽如此,AIDL IPC 调用不会在调用 setOnClickListener() 的主应用程序线程上进入,所以我仍然相信 Henry 是正确的,并且您的日志记录中有问题。
猜你喜欢
  • 1970-01-01
  • 2018-07-08
  • 2012-05-21
  • 2017-07-31
  • 1970-01-01
  • 1970-01-01
  • 2012-04-19
  • 2020-09-20
  • 1970-01-01
相关资源
最近更新 更多