简介:CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中……】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课🚀
专题四:Android15快速自定义与集成音效实战课🚀
专题五:Android15音频策略实战课🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例)🚀
人生格言:人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
🍉🍉🍉文章目录🍉🍉🍉
- 🌻1.前言
- 要点概括
- 🌻2.应用场景与用法
- 函数原型
- 参数说明
- 返回值
- 应用场景
- 🌻3.调用流程剖析
- 🌻3.1核心步骤
- 🌻3.2调用流程图
- 🌻3.3生命周期图
- 🌻4.实战应用案例
- 🌻5.一句话总结
🌻1.前言
本篇目的:
Linux PipeWire深度解析之pw_thread_loop_signal调用流程与实战。
要点概括
核心功能:唤醒正在pw_thread_loop_wait中等待条件变化的线程。
工作机制:基于Thread Loop内部条件变量发送通知,并可通过wait_for_accept参数等待对端调用pw_thread_loop_accept完成确认。
典型用途:跨线程同步PipeWireStream状态、等待Core连接结果、等待Registry事件、等待异步操作完成。
pw_thread_loop_signal的本质不是“触发PipeWire事件循环执行任务”,而是“通知等待线程某个共享条件已经发生变化”。它通常和pw_thread_loop_lock、pw_thread_loop_wait、pw_thread_loop_accept配合使用,用来在业务线程和PipeWireLoop线程之间建立同步点。
它不负责创建线程,不负责启动Loop,也不负责直接执行回调。Thread Loop的运行由pw_thread_loop_start负责,事件分发由内部pw_loop负责,signal只解决“等待方什么时候继续往下走”的问题。
它和pw_thread_loop_wait是一对接口。wait负责阻塞等待,signal负责唤醒等待。它和pw_thread_loop_accept也不同,accept只用于确认signal已经被等待方接收,通常配合wait_for_accept=true使用。它和pw_loop_invoke也不同,pw_loop_invoke用于把任务投递到Loop线程执行,而pw_thread_loop_signal只是线程同步通知。
🌻2.应用场景与用法
pw_thread_loop_signal
是PipeWireThread Loop API中用于唤醒等待线程的接口。
它位于PipeWire客户端多线程模型的同步路径中。应用使用pw_thread_loop_new创建Thread Loop后,可以把PipeWireContext、Core、Stream等对象放在该Thread Loop中运行。业务线程需要等待某个异步状态变化时,通常先持有Thread Loop锁,再调用pw_thread_loop_wait进入等待;当状态回调或其他线程更新共享状态后,再调用pw_thread_loop_signal唤醒等待方。
pw_thread_loop_signal用于唤醒正在pw_thread_loop_wait中等待的线程。
函数原型
voidpw_thread_loop_signal(structpw_thread_loop*loop,bool wait_for_accept);参数说明
structpw_thread_loop*loop;loop表示Thread Loop对象。
该对象通常由pw_thread_loop_new创建,并通过pw_thread_loop_start启动。调用pw_thread_loop_signal时,应用一般已经持有Thread Loop锁,避免共享状态更新和等待条件检查之间出现竞态。
bool wait_for_accept;wait_for_accept表示是否等待对端确认。
当该参数为false时,pw_thread_loop_signal发送通知后立即返回。调用方不关心等待方是否已经处理该通知。
当该参数为true时,pw_thread_loop_signal会等待对端调用pw_thread_loop_accept。这个模式适合需要同步确认的场景,例如调用方必须确认等待线程已经观察到状态变化后才能继续执行。
返回值
该函数没有返回值。
void调用完成只表示signal动作已经执行。它不表示PipeWire对象状态一定已经切换成功,也不表示异步操作一定已经完成。真正的完成条件应由应用自己的共享状态变量判断,例如connected、done、error、ready等标志。
应用场景
第一类场景是等待Stream状态变化。
应用创建pw_stream后,连接过程是异步的。业务线程可以等待state_changed回调更新状态,当回调收到目标状态后调用pw_thread_loop_signal唤醒业务线程。
第二类场景是等待Core连接完成。
PipeWire客户端连接Core后,部分信息需要通过事件回调返回。业务线程可以进入wait状态,回调线程收到结果后设置标志并signal。
第三类场景是等待Registry枚举完成。
客户端需要枚举Node、Device、Port、Factory等全局对象时,可以在registry_global回调中收集对象,在同步点到达后signal等待方继续执行。
第四类场景是控制线程和Loop线程同步退出。
应用关闭时,控制线程可以设置退出标志并signal等待方,等待方检查退出条件后跳出等待流程,避免Thread Loop销毁时仍有线程阻塞。
🌻3.调用流程剖析
🌻3.1核心步骤
1.应用调用pw_thread_loop_new创建Thread Loop对象。
2.应用调用pw_thread_loop_start启动后台Loop线程。
3.业务线程调用pw_thread_loop_lock持有Thread Loop锁。
4.业务线程检查共享状态变量,如果条件未满足,则调用pw_thread_loop_wait进入等待。
5.等待期间,pw_thread_loop_wait会释放锁,使Loop线程或其他线程可以继续更新共享状态。
6.状态回调或控制线程更新共享状态,例如ready、done、error、connected等。
7.状态更新方调用pw_thread_loop_signal通知等待线程。
8.等待线程被唤醒后重新获得Thread Loop锁。
9.等待线程再次检查共享状态,确认条件是否满足。
10.如果signal方设置wait_for_accept=true,等待线程处理完成后调用pw_thread_loop_accept确认接收。
11.signal方收到accept确认后继续执行。
12.业务线程处理完成后调用pw_thread_loop_unlock释放锁。
🌻3.2调用流程图
🌻3.3生命周期图
🌻4.实战应用案例
下面以“等待Stream进入目标状态”为例,说明pw_thread_loop_signal在真实PipeWire客户端中的用法。
应用创建Stream后,pw_stream_connect不会把所有状态同步返回给调用方。Stream状态变化会通过state_changed事件回调通知应用。业务线程如果需要等待Stream连接成功或失败,就可以使用Thread Loop同步机制。
structapp_data{structpw_thread_loop*loop;structpw_stream*stream;enumpw_stream_statestate;interror;bool done;};应用先定义共享状态。state记录Stream当前状态,error记录错误码,done表示等待条件是否完成。
staticvoidon_stream_state_changed(void*userdata,enumpw_stream_stateold,enumpw_stream_statestate,constchar*error){structapp_data*app=userdata;app->state=state;if(state==PW_STREAM_STATE_STREAMING||state==PW_STREAM_STATE_PAUSED){app->done=true;pw_thread_loop_signal(app->loop,false);}elseif(state==PW_STREAM_STATE_ERROR){app->error=-1;app->done=true;pw_thread_loop_signal(app->loop,false);}}state_changed回调中只做三件事。
第一,记录最新Stream状态。
第二,判断是否达到业务线程等待的目标条件。
第三,调用pw_thread_loop_signal唤醒等待线程。
这里使用wait_for_accept=false,因为业务线程只需要被唤醒后重新检查状态,不要求回调线程等待业务线程确认。
staticintwait_stream_ready(structapp_data*app){intres=0;pw_thread_loop_lock(app->loop);while(!app->done)pw_thread_loop_wait(app->loop);if(app->state==PW_STREAM_STATE_ERROR)res=app->error?app->error:-1;pw_thread_loop_unlock(app->loop);returnres;}等待侧必须使用while循环检查条件,而不是被唤醒后直接认为条件已经满足。线程同步中可能出现多次唤醒,也可能多个状态共用同一个signal路径。正确做法是:signal只负责唤醒,条件变量只负责等待,真正的业务状态必须由共享状态变量判断。
如果调用方需要确认等待线程已经处理过通知,可以使用wait_for_accept=true。
staticvoidnotify_and_wait_accept(structapp_data*app){pw_thread_loop_lock(app->loop);app->done=true;pw_thread_loop_signal(app->loop,true);pw_thread_loop_unlock(app->loop);}等待线程收到signal后,处理共享状态,并调用pw_thread_loop_accept确认。
staticvoidwait_and_accept(structapp_data*app){pw_thread_loop_lock(app->loop);while(!app->done)pw_thread_loop_wait(app->loop);/* * 这里已经观察到done状态,可以执行必要处理。 */pw_thread_loop_accept(app->loop);pw_thread_loop_unlock(app->loop);}wait_for_accept=true适合需要严格同步的路径。调用方发送signal后不会立即继续执行,而是等待等待方调用accept。这样可以保证等待方已经观察到共享状态变化。
工程上要特别注意三点。
第一,signal之前要先更新共享状态。否则等待线程可能被唤醒,但检查条件时仍然发现状态没有变化。
第二,等待侧要用while检查条件。不要把signal本身当成业务完成标志。
第三,不要把pw_thread_loop_signal当作任务投递接口。如果需要让Loop线程执行某个函数,应使用适合Loop任务投递的接口,而不是只发送条件通知。
🌻5.一句话总结
pw_thread_loop_signal是PipeWireThread Loop中的线程同步通知接口:它唤醒pw_thread_loop_wait等待方,必要时通过wait_for_accept和pw_thread_loop_accept形成确认握手,但它不负责执行Loop任务,也不直接改变PipeWire对象状态。