@@ -53,8 +53,21 @@ defmodule Predicator.Functions.Registry do
5353 """
5454 @ spec start_registry :: :ok
5555 def start_registry do
56- :ets . new ( @ registry_name , [ :set , :public , :named_table ] )
57- :ok
56+ case :ets . whereis ( @ registry_name ) do
57+ :undefined ->
58+ try do
59+ :ets . new ( @ registry_name , [ :set , :public , :named_table ] )
60+ :ok
61+ rescue
62+ ArgumentError ->
63+ # Another process created the table between our check and creation
64+ # This is fine, just return :ok
65+ :ok
66+ end
67+
68+ _table_exists ->
69+ :ok
70+ end
5871 end
5972
6073 @ doc """
@@ -89,8 +102,18 @@ defmodule Predicator.Functions.Registry do
89102 impl: impl
90103 }
91104
92- :ets . insert ( @ registry_name , { name , function_info } )
93- :ok
105+ # Handle potential race condition where table could be deleted between
106+ # ensure_registry_exists() and this insert operation
107+ try do
108+ :ets . insert ( @ registry_name , { name , function_info } )
109+ :ok
110+ rescue
111+ ArgumentError ->
112+ # Table was deleted between our check and insert, recreate and retry
113+ ensure_registry_exists ( )
114+ :ets . insert ( @ registry_name , { name , function_info } )
115+ :ok
116+ end
94117 end
95118
96119 @ doc """
@@ -112,21 +135,13 @@ defmodule Predicator.Functions.Registry do
112135 def call ( name , args , context ) when is_binary ( name ) and is_list ( args ) and is_map ( context ) do
113136 ensure_registry_exists ( )
114137
115- case :ets . lookup ( @ registry_name , name ) do
116- [ { _name , % { arity: arity , impl: impl } } ] ->
117- if length ( args ) == arity do
118- try do
119- impl . ( args , context )
120- rescue
121- error ->
122- { :error , "Function #{ name } () failed: #{ Exception . message ( error ) } " }
123- end
124- else
125- { :error , "Function #{ name } () expects #{ arity } arguments, got #{ length ( args ) } " }
126- end
127-
128- [ ] ->
129- { :error , "Unknown function: #{ name } " }
138+ try do
139+ do_lookup_and_call ( name , args , context )
140+ rescue
141+ ArgumentError ->
142+ # Table was deleted between our check and lookup, recreate and retry
143+ ensure_registry_exists ( )
144+ do_lookup_and_call ( name , args , context )
130145 end
131146 end
132147
@@ -139,9 +154,19 @@ defmodule Predicator.Functions.Registry do
139154 def list_functions do
140155 ensure_registry_exists ( )
141156
142- :ets . tab2list ( @ registry_name )
143- |> Enum . map ( fn { _key , function_info } -> function_info end )
144- |> Enum . sort_by ( & & 1 . name )
157+ try do
158+ :ets . tab2list ( @ registry_name )
159+ |> Enum . map ( fn { _key , function_info } -> function_info end )
160+ |> Enum . sort_by ( & & 1 . name )
161+ rescue
162+ ArgumentError ->
163+ # Table was deleted between our check and tab2list, recreate and retry
164+ ensure_registry_exists ( )
165+
166+ :ets . tab2list ( @ registry_name )
167+ |> Enum . map ( fn { _key , function_info } -> function_info end )
168+ |> Enum . sort_by ( & & 1 . name )
169+ end
145170 end
146171
147172 @ doc """
@@ -158,7 +183,15 @@ defmodule Predicator.Functions.Registry do
158183 @ spec function_registered? ( binary ( ) ) :: boolean ( )
159184 def function_registered? ( name ) when is_binary ( name ) do
160185 ensure_registry_exists ( )
161- :ets . member ( @ registry_name , name )
186+
187+ try do
188+ :ets . member ( @ registry_name , name )
189+ rescue
190+ ArgumentError ->
191+ # Table was deleted between our check and member, recreate and retry
192+ ensure_registry_exists ( )
193+ :ets . member ( @ registry_name , name )
194+ end
162195 end
163196
164197 @ doc """
@@ -169,8 +202,17 @@ defmodule Predicator.Functions.Registry do
169202 @ spec clear_registry ( ) :: :ok
170203 def clear_registry do
171204 ensure_registry_exists ( )
172- :ets . delete_all_objects ( @ registry_name )
173- :ok
205+
206+ try do
207+ :ets . delete_all_objects ( @ registry_name )
208+ :ok
209+ rescue
210+ ArgumentError ->
211+ # Table was deleted between our check and delete_all_objects, recreate and retry
212+ ensure_registry_exists ( )
213+ :ets . delete_all_objects ( @ registry_name )
214+ :ok
215+ end
174216 end
175217
176218 # Ensure the ETS table exists
@@ -180,4 +222,24 @@ defmodule Predicator.Functions.Registry do
180222 _table_exists -> :ok
181223 end
182224 end
225+
226+ # Helper function to lookup and call a function (extracted to eliminate duplicate code)
227+ defp do_lookup_and_call ( name , args , context ) do
228+ case :ets . lookup ( @ registry_name , name ) do
229+ [ { _name , % { arity: arity , impl: impl } } ] ->
230+ if length ( args ) == arity do
231+ try do
232+ impl . ( args , context )
233+ rescue
234+ error ->
235+ { :error , "Function #{ name } () failed: #{ Exception . message ( error ) } " }
236+ end
237+ else
238+ { :error , "Function #{ name } () expects #{ arity } arguments, got #{ length ( args ) } " }
239+ end
240+
241+ [ ] ->
242+ { :error , "Unknown function: #{ name } " }
243+ end
244+ end
183245end
0 commit comments