/* meow_ui_audio.c — App 侧独占音频客户端(SDK 公共件;见 meow_ui.h §11)。 * * 定位:App 只经本 API 喂 PCM 给 meow_audiod(独占 hw:0,0 守护)。audiod 不在一律失败 * (open 返回 MEOW_ERR_NOENT 等负错误码),不做隐式直开回退;调试可 env MEOW_AUDIO_DIRECT=1 走自带 * 直接 ALSA 直开(非正式能力)。 * * 传输:AF_UNIX SOCK_STREAM 到 audiod socket(缺省 /usr/data/meow_audio.sock;env * MEOW_AUDIO_SOCK 覆盖,宿主/qemu)。帧格式复用 meow_ipc.h 16B 头 + m_audio_* 负载(同一份)。 * * 线程模型(懒连 + 内部读线程): * - 首次 open/query 时 connect → 懒启动读线程(poll 读 audiod 下发帧)。 * - 读线程为唯一读方:PREEMPTED → 调 on_preempt 回调 + 标写死;OPEN_ACK/STATE/ACK(匹配 * 当前挂起请求)→ 拷贝到 g_resp、唤醒调用方(条件变量)。 * - 写路径(write/pause/resume/close)由 App 线程在另一方向写同一 fd(全双工,互不干扰)。 * * 格式归一:open 返回前记录 grant_rate/grant_ch/grant_fmt;meow_audio_write 把 native(open 参数) * 样本逐帧转成 grant 格式(mono→stereo 复制 / S16↔S32 定值升降位),分批 ≤4096B 发 MSG_AUDIO_WRITE。 * 无 SRC:grant_rate == native rate(audiod 把 DAC 原生率设为流率)。 */ #include "meow_ui.h" #include "meow_ipc.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #include #define UI_AUDIO_HAVE_ALSA 1 #endif #define AUD_SOCK_DEFAULT "/usr/data/meow_audio.sock" static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t g_cond = PTHREAD_COND_INITIALIZER; static int g_fd = -1; static pthread_t g_thr; static int g_thr_on = 0; static volatile int g_quit = 0; /* 当前流状态 */ static int g_has_stream = 0; static uint32_t g_sid = 0; static int g_dead = 0; /* 收到 PREEMPTED 后置 1:write 返回负值 */ static int g_own_rate = 0, g_own_ch = 0, g_own_fmt = 0; /* native(open 参数) */ static int g_grant_rate = 0, g_grant_ch = 0, g_grant_fmt = 0; static meow_audio_preempt_fn g_preempt_cb = NULL; static void *g_preempt_ctx = NULL; /* 挂起请求(读线程分发;一次只有一个) */ static uint32_t g_pending = 0; /* req_id,0=无挂起 */ static uint16_t g_resp_type = 0; static uint8_t g_resp[MEOW_IPC_MAX]; static uint32_t g_resp_len = 0; /* 调试直开(env MEOW_AUDIO_DIRECT=1) */ static int g_direct = 0; static int g_dir_fd = -1; static const char *sock_path(void) { const char *e = getenv("MEOW_AUDIO_SOCK"); return (e && e[0]) ? e : AUD_SOCK_DEFAULT; } static int connect_path(const char *path) { int fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) return -1; struct sockaddr_un a; memset(&a, 0, sizeof a); a.sun_family = AF_UNIX; strncpy(a.sun_path, path, sizeof a.sun_path - 1); if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { close(fd); return -1; } return fd; } static int write_all(int fd, const uint8_t *p, size_t n) { size_t off = 0; while (off < n) { ssize_t w = write(fd, p + off, n - off); if (w < 0) { if (errno == EINTR) continue; return -1; } if (w == 0) return -1; off += (size_t)w; } return 0; } static int read_all(int fd, uint8_t *p, size_t n) { size_t off = 0; while (off < n) { ssize_t r = read(fd, p + off, n - off); if (r < 0) { if (errno == EINTR) continue; return -1; } if (r == 0) return -1; off += (size_t)r; } return 0; } static int send_frame(int fd, uint16_t type, uint32_t req_id, const void *payload, uint32_t len) { uint8_t hdr[MEOW_IPC_HDR_LEN]; uint32_t magic = MEOW_IPC_MAGIC; uint16_t ver = MEOW_IPC_VERSION; memcpy(hdr + 0, &magic, 4); memcpy(hdr + 4, &ver, 2); memcpy(hdr + 6, &type, 2); memcpy(hdr + 8, &req_id, 4); memcpy(hdr + 12, &len, 4); if (write_all(fd, hdr, sizeof hdr) != 0) return -1; if (len && payload && write_all(fd, payload, len) != 0) return -1; return 0; } static int read_frame(int fd, uint16_t *type, uint8_t *rbuf, uint32_t rcap) { uint8_t hdr[MEOW_IPC_HDR_LEN]; if (read_all(fd, hdr, sizeof hdr) != 0) return -1; uint32_t magic, len; uint16_t ver; memcpy(&magic, hdr, 4); memcpy(&ver, hdr + 4, 2); memcpy(type, hdr + 6, 2); memcpy(&len, hdr + 12, 4); if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) return -1; if (len > rcap) len = rcap; if (len > 0 && read_all(fd, rbuf, len) != 0) return -1; return (int)len; } static void *audio_thread_main(void *arg); /* (调用方已持 mu)标记断线并关闭。 */ static void mark_dead_locked(void) { if (g_fd >= 0) { close(g_fd); g_fd = -1; } g_dead = 1; } /* (调用方已持 mu)懒连接 + 懒起读线程。成功 0;audiod 不在 -1。 */ static int conn_locked(void) { if (g_fd >= 0) return g_fd; if (g_direct) return -2; /* 调试直开:不连 socket */ const char *sp = sock_path(); int fd = connect_path(sp); if (fd < 0) return -1; int one = 1; setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one); g_fd = fd; if (!g_thr_on) { g_quit = 0; g_thr_on = 1; pthread_create(&g_thr, NULL, audio_thread_main, NULL); pthread_detach(g_thr); } return fd; } /* ---- 调试直开(MEOW_AUDIO_DIRECT=1,仅开发) ---- */ #ifdef UI_AUDIO_HAVE_ALSA static void pmask(struct snd_mask *m, unsigned bit) { m->bits[bit / 32] |= 1u << (bit % 32); } static void pivset(struct snd_interval *iv, unsigned val) { iv->min = iv->max = val; iv->integer = 1; iv->openmin = iv->openmax = 0; iv->empty = 0; } static int direct_alsa_open(int rate) { int fd = open("/dev/snd/pcmC0D0p", O_RDWR); if (fd < 0) return -1; struct snd_pcm_hw_params p; /* 「全放行」初始化(snd_pcm_hw_params_any 语义):memset 0 的 interval 会被内核当成 * 「只能取 0」约束 → refine 必空 → HW_PARAMS 恒 EINVAL(audiod 同款坑,勿回退)。 */ memset(&p, 0, sizeof p); unsigned nm = SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1; for (unsigned i = 0; i < nm; i++) for (unsigned b = 0; b < sizeof p.masks[i].bits / sizeof p.masks[i].bits[0]; b++) p.masks[i].bits[b] = ~0u; unsigned ni = SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1; for (unsigned i = 0; i < ni; i++) { p.intervals[i].min = 0; p.intervals[i].max = ~0u; } p.flags = 0; /* mask 先清零再单点(pmask 只 OR):全 1 的 access 会被内核 choose 成 MMAP_INTERLEAVED * → write() EINVAL(audiod 同款第二坑,勿回退)。 */ struct snd_mask *am = &p.masks[SNDRV_PCM_HW_PARAM_ACCESS - SNDRV_PCM_HW_PARAM_FIRST_MASK]; memset(am, 0, sizeof *am); pmask(am, SNDRV_PCM_ACCESS_RW_INTERLEAVED); struct snd_mask *fm = &p.masks[SNDRV_PCM_HW_PARAM_FORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK]; memset(fm, 0, sizeof *fm); pmask(fm, SNDRV_PCM_FORMAT_S16_LE); struct snd_mask *sm = &p.masks[SNDRV_PCM_HW_PARAM_SUBFORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK]; memset(sm, 0, sizeof *sm); pmask(sm, SNDRV_PCM_SUBFORMAT_STD); pivset(&p.intervals[SNDRV_PCM_HW_PARAM_CHANNELS - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL], 2); pivset(&p.intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL], (unsigned)rate); p.rmask = ~0u; p.info = 0; p.msbits = 0; p.rate_num = 0; p.rate_den = 0; if (ioctl(fd, SNDRV_PCM_IOCTL_HW_PARAMS, &p) != 0) { close(fd); return -1; } ioctl(fd, SNDRV_PCM_IOCTL_PREPARE, 0); return fd; } #endif /* ---- 格式转换 ---- */ static int fmt_bps(int fmt) { switch (fmt) { case AUDIO_FMT_S16: return 2; case AUDIO_FMT_S24: case AUDIO_FMT_S32: case AUDIO_FMT_FLOAT: return 4; default: return 2; } } static int32_t sample_from_native(const uint8_t *p, int fmt) { if (fmt == AUDIO_FMT_S16) { int16_t v; memcpy(&v, p, 2); return v; } int32_t v; memcpy(&v, p, 4); return v; /* S32/S24(32-in-32)/FLOAT 按 32 位采样 */ } static void sample_to_grant(uint8_t *p, int fmt, int32_t v) { if (fmt == AUDIO_FMT_S16) { int16_t x = (int16_t)v; memcpy(p, &x, 2); return; } memcpy(p, &v, 4); } static int32_t convert_sample(int32_t v, int nfmt, int gfmt) { int nb = (nfmt == AUDIO_FMT_S16) ? 16 : 32; int gb = (gfmt == AUDIO_FMT_S16) ? 16 : 32; if (gb > nb) return v << (gb - nb); if (gb < nb) return v >> (nb - gb); return v; } /* 发一批 WRITE(≤MEOW_IPC_MAX,帧对齐;调用方已持 mu) */ static int send_write_chunk(const uint8_t *p, uint32_t len) { if (g_fd < 0) { g_dead = 1; return -1; } if (send_frame(g_fd, MSG_AUDIO_WRITE, 0, p, len) != 0) { mark_dead_locked(); return -1; } return 0; } /* ---- 公开 API ---- */ int meow_audio_open(int rate, int ch, int fmt) { /* env 检查:调试直开 */ const char *de = getenv("MEOW_AUDIO_DIRECT"); g_direct = (de && de[0] == '1') ? 1 : 0; pthread_mutex_lock(&g_mu); if (g_has_stream) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_BUSY; } if (ch > 2 || ch < 1 || rate <= 0 || fmt < AUDIO_FMT_S16 || fmt > AUDIO_FMT_FLOAT || fmt == AUDIO_FMT_FLOAT) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_INVAL; } g_own_rate = rate; g_own_ch = ch; g_own_fmt = fmt; if (g_direct) { #ifdef UI_AUDIO_HAVE_ALSA int fd = direct_alsa_open(rate); if (fd < 0) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_NODEV; } g_dir_fd = fd; g_grant_rate = rate; g_grant_ch = 2; g_grant_fmt = AUDIO_FMT_S16; g_sid = 1; g_has_stream = 1; g_dead = 0; pthread_mutex_unlock(&g_mu); return 1; #else pthread_mutex_unlock(&g_mu); return MEOW_ERR_NODEV; #endif } int fd = conn_locked(); if (fd < 0) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_NOENT; } /* audiod 不在 */ m_audio_open_t o; memset(&o, 0, sizeof o); snprintf(o.app_id, sizeof o.app_id, "%s", getenv("MEOW_LOG_APP") ? getenv("MEOW_LOG_APP") : "app"); o.rate = (uint32_t)rate; o.ch = (uint16_t)ch; o.fmt = (uint16_t)fmt; uint32_t req = (g_pending == 0xFFFFFFFF) ? 1 : (g_pending + 1); /* 单调递增(单个挂起) */ g_pending = req; g_resp_len = 0; if (send_frame(g_fd, MSG_AUDIO_OPEN, req, &o, sizeof o) != 0) { mark_dead_locked(); pthread_mutex_unlock(&g_mu); return MEOW_ERR_IO; } /* 等响应(3s 超时) */ struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 3; while (g_pending == req) { int r = pthread_cond_timedwait(&g_cond, &g_mu, &ts); if (r != 0) { mark_dead_locked(); pthread_mutex_unlock(&g_mu); return MEOW_ERR_TIMEOUT; } } if (g_resp_type != MSG_AUDIO_OPEN_ACK || g_resp_len < sizeof(m_audio_open_ack_t)) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_IO; } m_audio_open_ack_t a; memcpy(&a, g_resp, sizeof a); if (a.err != MEOW_OK) { pthread_mutex_unlock(&g_mu); return (int)a.err; } g_sid = a.stream_id; g_grant_rate = (int)a.grant_rate; g_grant_ch = (int)a.grant_ch; g_grant_fmt = (int)a.grant_fmt; g_has_stream = 1; g_dead = 0; pthread_mutex_unlock(&g_mu); return (int)g_sid; } int meow_audio_write(const void *pcm, int frames) { if (!pcm || frames <= 0) return 0; pthread_mutex_lock(&g_mu); if (g_direct) { size_t nb = (size_t)frames * (size_t)g_grant_ch * (size_t)fmt_bps(g_grant_fmt); const uint8_t *p = (const uint8_t *)pcm; size_t off = 0; while (off < nb) { ssize_t w = write(g_dir_fd, p + off, nb - off); if (w < 0) { if (errno == EINTR) continue; pthread_mutex_unlock(&g_mu); return -1; } off += (size_t)w; } pthread_mutex_unlock(&g_mu); return frames; } if (!g_has_stream || g_dead || g_fd < 0) { pthread_mutex_unlock(&g_mu); return -1; } /* 归一:native → grant */ int ownfs = g_own_ch * fmt_bps(g_own_fmt); int gfs = g_grant_ch * fmt_bps(g_grant_fmt); static uint8_t out[MEOW_IPC_MAX]; size_t ofs = 0; int done_frames = 0; const uint8_t *np = (const uint8_t *)pcm; for (int i = 0; i < frames; i++) { /* 取 native 帧样本 */ int32_t s[2]; for (int c = 0; c < g_own_ch && c < 2; c++) s[c] = sample_from_native(np + (size_t)i * ownfs + (size_t)c * fmt_bps(g_own_fmt), g_own_fmt); /* 整帧才能进 payload(每帧不得跨两个 WRITE 分片) */ if (ofs + (size_t)gfs > sizeof out) { if (send_write_chunk(out, (uint32_t)ofs) != 0) { pthread_mutex_unlock(&g_mu); return -1; } ofs = 0; } /* 生成 grant 帧:own_ch==1 → 复制;own_ch==2 → 直通 */ for (int c = 0; c < g_grant_ch && c < 2; c++) { int32_t v = (g_own_ch == 1) ? s[0] : s[c]; v = convert_sample(v, g_own_fmt, g_grant_fmt); sample_to_grant(out + ofs, g_grant_fmt, v); ofs += (size_t)fmt_bps(g_grant_fmt); } done_frames++; } if (ofs && send_write_chunk(out, (uint32_t)ofs) != 0) { pthread_mutex_unlock(&g_mu); return -1; } pthread_mutex_unlock(&g_mu); return done_frames; } int meow_audio_pause(int sid, int paused) { pthread_mutex_lock(&g_mu); if (g_direct) { pthread_mutex_unlock(&g_mu); return 0; } if (!g_has_stream || g_sid != (uint32_t)sid) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_NOENT; } m_audio_stream_t s; s.stream_id = (uint32_t)sid; uint32_t req = g_pending + 1; g_pending = req; if (send_frame(g_fd, paused ? MSG_AUDIO_PAUSE : MSG_AUDIO_RESUME, req, &s, sizeof s) != 0) { mark_dead_locked(); pthread_mutex_unlock(&g_mu); return MEOW_ERR_IO; } struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 3; while (g_pending == req) { if (pthread_cond_timedwait(&g_cond, &g_mu, &ts) != 0) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_TIMEOUT; } } pthread_mutex_unlock(&g_mu); return 0; } int meow_audio_close(int sid) { pthread_mutex_lock(&g_mu); if (g_direct) { g_has_stream = 0; if (g_dir_fd >= 0) close(g_dir_fd); g_dir_fd = -1; pthread_mutex_unlock(&g_mu); return 0; } if (g_has_stream && g_sid == (uint32_t)sid) { m_audio_stream_t s; s.stream_id = (uint32_t)sid; uint32_t req = g_pending + 1; g_pending = req; if (send_frame(g_fd, MSG_AUDIO_STOP, req, &s, sizeof s) == 0) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 3; while (g_pending == req) { if (pthread_cond_timedwait(&g_cond, &g_mu, &ts) != 0) break; } } g_has_stream = 0; g_sid = 0; g_dead = 0; } pthread_mutex_unlock(&g_mu); return 0; } /* 从 audiod STATE 文本解析关键字段 */ static int state_text_parse(const char *t, meow_audio_state *o) { memset(o, 0, sizeof *o); o->route_port = -1; const char *p; if ((p = strstr(t, "active="))) o->active = atoi(p + 7); if ((p = strstr(t, "app="))) { /* app_id 只取到下一个空白(状态行是空格分隔 k=v,整体拷贝会吞掉后续字段) */ p += 4; size_t k = 0; while (p[k] && p[k] != ' ' && p[k] != '\n' && p[k] != '\t' && k < sizeof o->app_id - 1) { o->app_id[k] = p[k]; k++; } o->app_id[k] = 0; } if ((p = strstr(t, "sid="))) o->stream_id = (uint32_t)atoi(p + 4); if ((p = strstr(t, "rate="))) o->rate = (uint32_t)atoi(p + 5); if ((p = strstr(t, "ch="))) o->ch = (uint16_t)atoi(p + 3); if ((p = strstr(t, "fmt="))) { int f = 0; if (strncmp(p + 4, "S16", 3) == 0) f = AUDIO_FMT_S16; else if (strncmp(p + 4, "S24", 3) == 0) f = AUDIO_FMT_S24; else if (strncmp(p + 4, "S32", 3) == 0) f = AUDIO_FMT_S32; o->fmt = (uint16_t)f; } if ((p = strstr(t, "written="))) o->written_frames = (uint64_t)strtoull(p + 8, NULL, 10); if ((p = strstr(t, "played="))) o->played_frames = (uint64_t)strtoull(p + 7, NULL, 10); if ((p = strstr(t, "underrun="))) o->underrun = (uint32_t)atoi(p + 9); if ((p = strstr(t, "paused="))) o->paused = atoi(p + 7); if ((p = strstr(t, "port="))) o->route_port = atoi(p + 5); if ((p = strstr(t, "vol="))) o->vol = atoi(p + 4); if (o->app_id[0] == '\0') snprintf(o->app_id, sizeof o->app_id, "-"); return 0; } int meow_audio_query(meow_audio_state *out) { if (!out) return MEOW_ERR_INVAL; pthread_mutex_lock(&g_mu); if (g_direct) { memset(out, 0, sizeof *out); out->active = g_has_stream; out->stream_id = g_sid; out->rate = (uint32_t)g_grant_rate; out->ch = (uint16_t)g_grant_ch; out->fmt = (uint16_t)g_grant_fmt; out->route_port = -1; pthread_mutex_unlock(&g_mu); return 0; } int fd = conn_locked(); if (fd < 0) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_NOENT; } uint32_t req = g_pending + 1; g_pending = req; if (send_frame(g_fd, MSG_AUDIO_QUERY, req, NULL, 0) != 0) { mark_dead_locked(); pthread_mutex_unlock(&g_mu); return MEOW_ERR_IO; } struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 3; while (g_pending == req) { if (pthread_cond_timedwait(&g_cond, &g_mu, &ts) != 0) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_TIMEOUT; } } if (g_resp_type != MSG_AUDIO_STATE) { pthread_mutex_unlock(&g_mu); return MEOW_ERR_IO; } g_resp[g_resp_len < MEOW_IPC_MAX - 1 ? g_resp_len : MEOW_IPC_MAX - 1] = 0; state_text_parse((const char *)g_resp, out); pthread_mutex_unlock(&g_mu); return 0; } void meow_audio_on_preempt(meow_audio_preempt_fn cb, void *ctx) { pthread_mutex_lock(&g_mu); g_preempt_cb = cb; g_preempt_ctx = ctx; int need = !g_thr_on && !g_direct; if (need) { g_quit = 0; g_thr_on = 1; } pthread_mutex_unlock(&g_mu); if (need) { pthread_create(&g_thr, NULL, audio_thread_main, NULL); pthread_detach(g_thr); } } /* 读线程主体:poll 读 audiod 下发帧 → 分发 PREEMPTED / 挂起响应。 * **不**自行 connect——连接由调用方(open/query 经 conn_locked)建立并启动本线程; * 这里只等 g_fd 就绪后 poll(避免与调用方 conn_locked 竞争出新 socket)。 */ static void *audio_thread_main(void *arg) { (void)arg; while (!g_quit) { pthread_mutex_lock(&g_mu); int fd = g_fd; pthread_mutex_unlock(&g_mu); if (fd < 0) { usleep(300000); continue; } struct pollfd p; p.fd = fd; p.events = POLLIN; p.revents = 0; int pr = poll(&p, 1, 200); if (pr < 0 && errno != EINTR) { pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu); continue; } if (pr > 0 && (p.revents & (POLLHUP | POLLERR | POLLNVAL))) { pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu); continue; } if (pr > 0 && (p.revents & POLLIN)) { uint8_t rbuf[MEOW_IPC_MAX]; uint16_t type; int n = read_frame(fd, &type, rbuf, sizeof rbuf); if (n < 0) { pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu); continue; } if (type == MSG_AUDIO_PREEMPTED && n >= (int)sizeof(m_audio_preempted_t)) { m_audio_preempted_t pm; memcpy(&pm, rbuf, sizeof pm); meow_audio_preempt_fn cb; void *ctx; pthread_mutex_lock(&g_mu); cb = g_preempt_cb; ctx = g_preempt_ctx; if (pm.stream_id == g_sid) { g_has_stream = 0; g_dead = 1; g_sid = 0; } pthread_mutex_unlock(&g_mu); if (cb) cb(pm.by_app, ctx); } else if (type == MSG_AUDIO_OPEN_ACK || type == MSG_ACK || type == MSG_AUDIO_STATE) { pthread_mutex_lock(&g_mu); if (g_pending) { g_resp_type = type; if ((uint32_t)n > MEOW_IPC_MAX) n = (int)MEOW_IPC_MAX; memcpy(g_resp, rbuf, (size_t)n); g_resp_len = (uint32_t)n; g_pending = 0; pthread_cond_signal(&g_cond); } pthread_mutex_unlock(&g_mu); } /* 其它类型丢弃 */ } } pthread_mutex_lock(&g_mu); mark_dead_locked(); pthread_mutex_unlock(&g_mu); return NULL; }