@@ -40,29 +40,13 @@ namespace srt::g2p {
4040
4141 srt::core::Expected<void > Manager::loadTasksForCategory (const std::string &category) {
4242 auto &impl = *static_cast <PackageManager::Impl *>(_impl.get ());
43+ std::unique_lock<std::shared_mutex> lock (impl.m_tasks_mtx );
4344
44- // m_contextStates / m_contextModuleInfos are guarded by m_su_mtx, while
45- // m_tasks is guarded by m_tasks_mtx. Snapshot the Ready contexts and
46- // their module infos under m_su_mtx (shared) first, then populate
47- // m_tasks under m_tasks_mtx. Never hold both locks at once (matches the
48- // convention in removeContextsByPrefix).
49- std::vector<std::pair<srt::core::ContextKey,
50- std::vector<srt::dependency::ModuleMetadata>>>
51- readyContexts;
52- {
53- std::shared_lock<std::shared_mutex> suLock (impl.m_su_mtx );
54- for (const auto &[ctxKey, state] : impl.m_contextStates ) {
55- if (state != ContextState::Ready)
56- continue ;
57- auto it = impl.m_contextModuleInfos .find (ctxKey);
58- if (it == impl.m_contextModuleInfos .end ())
59- continue ;
60- readyContexts.emplace_back (ctxKey, it->second );
61- }
62- }
45+ for (const auto &[ctxKey, state] : impl.m_contextStates ) {
46+ if (state != ContextState::Ready)
47+ continue ;
6348
64- std::unique_lock<std::shared_mutex> lock (impl.m_tasks_mtx );
65- for (const auto &[ctxKey, ctxModuleInfos] : readyContexts) {
49+ auto &ctxModuleInfos = impl.m_contextModuleInfos [ctxKey];
6650 for (const auto &moduleInfo : ctxModuleInfos) {
6751 if (moduleInfo.type != category)
6852 continue ;
@@ -109,78 +93,86 @@ namespace srt::g2p {
10993 " Manager::initialize() has already been called" );
11094 }
11195
112- // Phase 1: Default context
96+ // Phase 1: Default context (official G2P).
97+ // Allow empty default context: when no official G2P packages are
98+ // registered (voicebank-only deployment), skip Phase 1 entirely and
99+ // let voicebank contexts (Phase 2) initialize independently. Attempts
100+ // to use the default context will return G2pContextNotFound. This
101+ // honors ds-session.md §210: "某 G2P module 初始化失败:仅关联
102+ // singer-language Disabled,其他语言/声库仍可发布".
113103 {
114104 srt::core::ContextKey defaultCtx (" " );
115105 const auto moduleInfos = this ->getModuleMetadatas (defaultCtx);
116106 if (moduleInfos.empty ()) {
117- return Error (ErrorCode::G2pInitializationError,
118- " Ord-1: Default context initialization failed: no modules found" );
119- }
120-
121- auto compatibleModules = filterCompatibleModules (moduleInfos);
122- if (compatibleModules.empty ()) {
123- return Error (ErrorCode::G2pInitializationError,
124- " Ord-1: Default context has no compatible modules after Level check" );
125- }
126-
127- auto &depGraph = impl.m_contextDependencyGraphs [srt::core::ContextKey (" " )];
128- depGraph.clear ();
129- for (const auto &info : compatibleModules)
130- depGraph.addModule (info);
107+ g2pLog.srtWarning (
108+ " Ord-1: Default context has no modules; official G2P unavailable "
109+ " (voicebank-private G2P still initializes in Phase 2)" );
110+ } else {
111+ auto compatibleModules = filterCompatibleModules (moduleInfos);
112+ if (compatibleModules.empty ()) {
113+ g2pLog.srtWarning (
114+ " Ord-1: Default context has no compatible modules after Level check; "
115+ " official G2P unavailable" );
116+ } else {
117+ auto &depGraph = impl.m_contextDependencyGraphs [srt::core::ContextKey (" " )];
118+ depGraph.clear ();
119+ for (const auto &info : compatibleModules)
120+ depGraph.addModule (info);
121+
122+ if (!depGraph.buildGraph ()) {
123+ return Error (ErrorCode::G2pDependencyError,
124+ " Ord-1: Failed to build dependency graph for default context" );
125+ }
131126
132- if (! depGraph.buildGraph ()) {
133- return Error (ErrorCode::G2pDependencyError,
134- " Ord-1: Failed to build dependency graph for default context" );
135- }
127+ if (const auto cycles = depGraph.findCycles (); !cycles. empty ()) {
128+ return Error (ErrorCode::G2pDependencyError,
129+ " Ord-1: Circular dependencies detected in default context" );
130+ }
136131
137- if (const auto cycles = depGraph.findCycles (); !cycles.empty ()) {
138- return Error (ErrorCode::G2pDependencyError,
139- " Ord-1: Circular dependencies detected in default context" );
140- }
132+ auto packageOrder = depGraph.getPackageInitializationOrder ();
133+ int failedPkgCount = 0 ;
134+ std::vector<std::string> pkgErrors;
135+ for (const auto &packageInfo : packageOrder) {
136+ auto exp = this ->open (packageInfo.packagePath );
137+ if (!exp) {
138+ pkgErrors.push_back (packageInfo.packageId + " : " + exp.error ().message ());
139+ failedPkgCount++;
140+ continue ;
141+ }
141142
142- auto packageOrder = depGraph.getPackageInitializationOrder ();
143- int failedPkgCount = 0 ;
144- std::vector<std::string> pkgErrors;
145- for (const auto &packageInfo : packageOrder) {
146- auto exp = this ->open (packageInfo.packagePath );
147- if (!exp) {
148- pkgErrors.push_back (packageInfo.packageId + " : " + exp.error ().message ());
149- failedPkgCount++;
150- continue ;
151- }
143+ Package pkg = exp.take ();
144+ if (!pkg.isLoaded ()) {
145+ pkgErrors.push_back (packageInfo.packageId + " : " + pkg.error ().message ());
146+ failedPkgCount++;
147+ continue ;
148+ }
152149
153- Package pkg = exp.take ();
154- if (!pkg.isLoaded ()) {
155- pkgErrors.push_back (packageInfo.packageId + " : " + pkg.error ().message ());
156- failedPkgCount++;
157- continue ;
158- }
150+ for (const auto &moduleInfo : packageInfo.initializationOrder ) {
151+ auto taskExp = createModuleTask (moduleInfo, pkg);
152+ if (taskExp) {
153+ const auto fqid = ContextUtils::formatFqid (defaultCtx, moduleInfo.moduleId );
154+ if (auto *cate = this ->category (moduleInfo.type )) {
155+ cate->addObject (fqid, taskExp.value ());
156+ }
157+ } else {
158+ g2pLog.srtWarning (" G2P task init failed: package=%1, module=%2, type=%3 - %4" ,
159+ packageInfo.packageId , moduleInfo.moduleId ,
160+ moduleInfo.type , taskExp.error ().message ());
161+ }
162+ }
163+ }
159164
160- for (const auto &moduleInfo : packageInfo.initializationOrder ) {
161- auto taskExp = createModuleTask (moduleInfo, pkg);
162- if (taskExp) {
163- const auto fqid = ContextUtils::formatFqid (defaultCtx, moduleInfo.moduleId );
164- if (auto *cate = this ->category (moduleInfo.type )) {
165- cate->addObject (fqid, taskExp.value ());
165+ if (failedPkgCount > 0 && packageOrder.size () == static_cast <size_t >(failedPkgCount)) {
166+ std::string detail = " Ord-1: All packages failed to load in default context" ;
167+ for (const auto &e : pkgErrors) {
168+ detail += " \n " + e;
166169 }
167- } else {
168- g2pLog.srtWarning (" G2P task init failed: package=%1, module=%2, type=%3 - %4" ,
169- packageInfo.packageId , moduleInfo.moduleId ,
170- moduleInfo.type , taskExp.error ().message ());
170+ return Error (ErrorCode::G2pInitializationError, detail);
171171 }
172- }
173- }
174172
175- if (failedPkgCount > 0 && packageOrder.size () == static_cast <size_t >(failedPkgCount)) {
176- std::string detail = " Ord-1: All packages failed to load in default context" ;
177- for (const auto &e : pkgErrors) {
178- detail += " \n " + e;
173+ impl.m_contextStates [defaultCtx] = ContextState::Ready;
179174 }
180- return Error (ErrorCode::G2pInitializationError, detail);
181175 }
182-
183- impl.m_contextStates [defaultCtx] = ContextState::Ready;
184176 }
185177
186178 // Phase 2: Non-default contexts
0 commit comments