@@ -84,7 +84,28 @@ RocketMQProducer::RocketMQProducer(const Napi::CallbackInfo& info)
8484}
8585
8686RocketMQProducer::~RocketMQProducer () {
87- producer_.shutdown ();
87+ SafeShutdown ();
88+ }
89+
90+ void RocketMQProducer::SafeShutdown () {
91+ std::lock_guard<std::mutex> lock (state_mutex_);
92+
93+ if (is_destroyed_.exchange (true )) {
94+ return ; // Already destroyed
95+ }
96+
97+ if (is_started_.load () && !is_shutting_down_.exchange (true )) {
98+ try {
99+ producer_.shutdown ();
100+ } catch (const std::exception& e) {
101+ // Log error but don't throw in destructor
102+ fprintf (stderr, " [RocketMQ] Warning: Producer shutdown failed in destructor: %s\n " , e.what ());
103+ } catch (...) {
104+ fprintf (stderr, " [RocketMQ] Warning: Unknown error during producer shutdown in destructor\n " );
105+ }
106+ }
107+
108+ is_started_.store (false );
88109}
89110
90111void RocketMQProducer::SetOptions (const Napi::Object& options) {
@@ -179,11 +200,30 @@ class ProducerStartWorker : public Napi::AsyncWorker {
179200 RocketMQProducer* wrapper)
180201 : Napi::AsyncWorker(callback),
181202 wrapper_ref_ (Napi::Persistent(wrapper->Value ())),
182- producer_(&wrapper->producer_) {}
203+ producer_(&wrapper->producer_),
204+ wrapper_(wrapper) {}
183205
184206 void Execute () override {
207+ std::lock_guard<std::mutex> lock (wrapper_->state_mutex_ );
208+
209+ if (wrapper_->is_destroyed_ .load ()) {
210+ SetError (" Producer has been destroyed" );
211+ return ;
212+ }
213+
214+ if (wrapper_->is_started_ .load ()) {
215+ SetError (" Producer is already started" );
216+ return ;
217+ }
218+
219+ if (wrapper_->is_shutting_down_ .load ()) {
220+ SetError (" Producer is shutting down" );
221+ return ;
222+ }
223+
185224 try {
186225 producer_->start ();
226+ wrapper_->is_started_ .store (true );
187227 } catch (const std::exception& e) {
188228 SetError (e.what ());
189229 }
@@ -192,6 +232,7 @@ class ProducerStartWorker : public Napi::AsyncWorker {
192232 private:
193233 Napi::ObjectReference wrapper_ref_;
194234 rocketmq::DefaultMQProducer* producer_;
235+ RocketMQProducer* wrapper_;
195236};
196237
197238Napi::Value RocketMQProducer::Start (const Napi::CallbackInfo& info) {
@@ -216,19 +257,41 @@ class ProducerShutdownWorker : public Napi::AsyncWorker {
216257 RocketMQProducer* wrapper)
217258 : Napi::AsyncWorker(callback),
218259 wrapper_ref_ (Napi::Persistent(wrapper->Value ())),
219- producer_(&wrapper->producer_) {}
260+ producer_(&wrapper->producer_),
261+ wrapper_(wrapper) {}
220262
221263 void Execute () override {
264+ std::lock_guard<std::mutex> lock (wrapper_->state_mutex_ );
265+
266+ if (wrapper_->is_destroyed_ .load ()) {
267+ SetError (" Producer has been destroyed" );
268+ return ;
269+ }
270+
271+ if (!wrapper_->is_started_ .load ()) {
272+ SetError (" Producer is not started" );
273+ return ;
274+ }
275+
276+ if (wrapper_->is_shutting_down_ .exchange (true )) {
277+ SetError (" Producer is already shutting down" );
278+ return ;
279+ }
280+
222281 try {
223282 producer_->shutdown ();
283+ wrapper_->is_started_ .store (false );
284+ wrapper_->is_shutting_down_ .store (false ); // Reset shutdown flag after successful shutdown
224285 } catch (const std::exception& e) {
286+ wrapper_->is_shutting_down_ .store (false ); // Reset on error
225287 SetError (e.what ());
226288 }
227289 }
228290
229291 private:
230292 Napi::ObjectReference wrapper_ref_;
231293 rocketmq::DefaultMQProducer* producer_;
294+ RocketMQProducer* wrapper_;
232295};
233296
234297Napi::Value RocketMQProducer::Shutdown (const Napi::CallbackInfo& info) {
@@ -265,7 +328,8 @@ class ProducerSendCallback : public rocketmq::AutoDeleteSendCallback {
265328 : prevent_gc_(new Napi::ObjectReference(std::move(producer_ref))),
266329 cleanup_ctx_ (nullptr ),
267330 callback_(),
268- prevent_prevent_release_(false ) {
331+ prevent_prevent_release_(false ),
332+ callback_scheduled_(false ) {
269333 std::unique_ptr<CleanupContext> ctx (new CleanupContext ());
270334 callback_ = Callback::New (env,
271335 callback,
@@ -280,7 +344,13 @@ class ProducerSendCallback : public rocketmq::AutoDeleteSendCallback {
280344
281345 ~ProducerSendCallback () {
282346 if (!prevent_prevent_release_.exchange (true )) {
283- callback_.Abort ();
347+ try {
348+ callback_.Abort ();
349+ } catch (const std::exception& e) {
350+ fprintf (stderr, " [RocketMQ] Warning: Error aborting send callback: %s\n " , e.what ());
351+ } catch (...) {
352+ fprintf (stderr, " [RocketMQ] Warning: Unknown error aborting send callback\n " );
353+ }
284354 }
285355 }
286356
@@ -297,6 +367,11 @@ class ProducerSendCallback : public rocketmq::AutoDeleteSendCallback {
297367 private:
298368 void ScheduleCallback (std::unique_ptr<rocketmq::SendResult> result,
299369 std::exception_ptr exception) {
370+ // Prevent multiple callback scheduling
371+ if (callback_scheduled_.exchange (true )) {
372+ return ;
373+ }
374+
300375 auto prevent_gc = std::move (prevent_gc_);
301376 auto * data = new CallbackData{
302377 std::move (result),
@@ -315,12 +390,18 @@ class ProducerSendCallback : public rocketmq::AutoDeleteSendCallback {
315390 status = callback_.BlockingCall (data);
316391#endif
317392 if (status != napi_ok) {
318- fprintf (stderr, " Failed to schedule JavaScript callback: %d\n " , status);
393+ fprintf (stderr, " [RocketMQ] Failed to schedule JavaScript callback: %d\n " , status);
319394 cleanup_ctx_->pending .reset (data);
320395 }
321396
322397 if (!prevent_prevent_release_.exchange (true )) {
323- callback_.Release ();
398+ try {
399+ callback_.Release ();
400+ } catch (const std::exception& e) {
401+ fprintf (stderr, " [RocketMQ] Warning: Error releasing send callback: %s\n " , e.what ());
402+ } catch (...) {
403+ fprintf (stderr, " [RocketMQ] Warning: Unknown error releasing send callback\n " );
404+ }
324405 }
325406 }
326407
@@ -357,6 +438,10 @@ class ProducerSendCallback : public rocketmq::AutoDeleteSendCallback {
357438 }
358439 } catch (const Napi::Error& e) {
359440 e.ThrowAsJavaScriptException ();
441+ } catch (const std::exception& e) {
442+ fprintf (stderr, " [RocketMQ] Warning: Exception in send callback: %s\n " , e.what ());
443+ } catch (...) {
444+ fprintf (stderr, " [RocketMQ] Warning: Unknown exception in send callback\n " );
360445 }
361446 }
362447
@@ -372,12 +457,13 @@ class ProducerSendCallback : public rocketmq::AutoDeleteSendCallback {
372457 CleanupContext* cleanup_ctx_;
373458 Callback callback_;
374459 std::atomic<bool > prevent_prevent_release_;
460+ std::atomic<bool > callback_scheduled_;
375461};
376462
377463Napi::Value RocketMQProducer::Send (const Napi::CallbackInfo& info) {
378464 Napi::Env env = info.Env ();
379465
380- // Check if required parameters are provided
466+ // Check if required parameters are provided FIRST (before state checks)
381467 if (info.Length () < 4 ) {
382468 Napi::TypeError::New (env, " Wrong number of arguments" ).ThrowAsJavaScriptException ();
383469 return env.Undefined ();
@@ -393,6 +479,25 @@ Napi::Value RocketMQProducer::Send(const Napi::CallbackInfo& info) {
393479 return env.Undefined ();
394480 }
395481
482+ // Check if producer is in valid state AFTER parameter validation
483+ {
484+ std::lock_guard<std::mutex> lock (state_mutex_);
485+ if (is_destroyed_.load ()) {
486+ Napi::Error::New (env, " Producer has been destroyed" ).ThrowAsJavaScriptException ();
487+ return env.Undefined ();
488+ }
489+
490+ if (!is_started_.load ()) {
491+ Napi::Error::New (env, " Producer is not started" ).ThrowAsJavaScriptException ();
492+ return env.Undefined ();
493+ }
494+
495+ if (is_shutting_down_.load ()) {
496+ Napi::Error::New (env, " Producer is shutting down" ).ThrowAsJavaScriptException ();
497+ return env.Undefined ();
498+ }
499+ }
500+
396501 rocketmq::MQMessage message = [&]() {
397502 Napi::String topic = info[0 ].As <Napi::String>();
398503 Napi::Value body = info[1 ];
0 commit comments