diff --git a/changelogs/unreleased/gh-4450-fix-crash-on-logrotate-and-exit.md b/changelogs/unreleased/gh-4450-fix-crash-on-logrotate-and-exit.md
new file mode 100644
index 0000000000000000000000000000000000000000..3c3e4f71937e26703da9848b5ac0f242e9f21d93
--- /dev/null
+++ b/changelogs/unreleased/gh-4450-fix-crash-on-logrotate-and-exit.md
@@ -0,0 +1,3 @@
+## bugfix/core
+
+* Fixed crash on log rotate and immediate application exit (gh-4450).
diff --git a/src/lib/core/say.c b/src/lib/core/say.c
index e929146a7a6007860aef20f343da60ebbc80c760..c9f7dd415fce6d8cbaa41aa8abed3a6a4801798b 100644
--- a/src/lib/core/say.c
+++ b/src/lib/core/say.c
@@ -330,10 +330,15 @@ static int
 logrotate_cb(struct coio_task *ptr)
 {
 	struct rotate_task *task = (struct rotate_task *) ptr;
-	if (log_rotate(task->log) < 0) {
+	struct log *log = task->log;
+	if (log_rotate(log) < 0)
 		diag_log();
-	}
-	ev_async_send(task->loop, &task->log->log_async);
+	tt_pthread_mutex_lock(&log->rotate_mutex);
+	assert(log->rotating_threads > 0);
+	log->rotating_threads--;
+	if (log->rotating_threads == 0)
+		tt_pthread_cond_signal(&log->rotate_cond);
+	tt_pthread_mutex_unlock(&log->rotate_mutex);
 	return 0;
 }
 
@@ -346,16 +351,6 @@ logrotate_cleanup_cb(struct coio_task *ptr)
 	return 0;
 }
 
-static void
-log_rotate_async_cb(struct ev_loop *loop, struct ev_async *watcher, int events)
-{
-	(void)loop;
-	(void)events;
-	struct log *log = container_of(watcher, struct log, log_async);
-	log->rotating_threads--;
-	fiber_cond_signal(&log->rotate_cond);
-}
-
 void
 say_logrotate(struct ev_loop *loop, struct ev_signal *w, int revents)
 {
@@ -373,8 +368,9 @@ say_logrotate(struct ev_loop *loop, struct ev_signal *w, int revents)
 			diag_log();
 			continue;
 		}
-		ev_async_start(loop(), &log->log_async);
+		tt_pthread_mutex_lock(&log->rotate_mutex);
 		log->rotating_threads++;
+		tt_pthread_mutex_unlock(&log->rotate_mutex);
 		coio_task_create(&task->base, logrotate_cb, logrotate_cleanup_cb);
 		task->log = log;
 		task->loop = loop();
@@ -661,8 +657,8 @@ log_create(struct log *log, const char *init_str, int nonblock)
 	log->level = S_INFO;
 	log->rotating_threads = 0;
 	log->on_log = NULL;
-	fiber_cond_create(&log->rotate_cond);
-	ev_async_init(&log->log_async, log_rotate_async_cb);
+	tt_pthread_mutex_init(&log->rotate_mutex, NULL);
+	tt_pthread_cond_init(&log->rotate_cond, NULL);
 	setvbuf(stderr, NULL, _IONBF, 0);
 
 	if (init_str != NULL) {
@@ -1241,8 +1237,10 @@ void
 log_destroy(struct log *log)
 {
 	assert(log != NULL);
+	tt_pthread_mutex_lock(&log->rotate_mutex);
 	while(log->rotating_threads > 0)
-		fiber_cond_wait(&log->rotate_cond);
+		tt_pthread_cond_wait(&log->rotate_cond, &log->rotate_mutex);
+	tt_pthread_mutex_unlock(&log->rotate_mutex);
 	pm_atomic_store(&log->type, SAY_LOGGER_BOOT);
 
 	if (log->fd != -1)
@@ -1250,8 +1248,8 @@ log_destroy(struct log *log)
 	free(log->syslog_ident);
 	free(log->path);
 	rlist_del_entry(log, in_log_list);
-	ev_async_stop(loop(), &log->log_async);
-	fiber_cond_destroy(&log->rotate_cond);
+	tt_pthread_mutex_destroy(&log->rotate_mutex);
+	tt_pthread_cond_destroy(&log->rotate_cond);
 }
 
 int
diff --git a/src/lib/core/say.h b/src/lib/core/say.h
index 6d7d170df7b1a2bb126781171eee30dd0fbdac46..7cb53c95fc1c5fd15489cb018033e8009642e2eb 100644
--- a/src/lib/core/say.h
+++ b/src/lib/core/say.h
@@ -168,16 +168,15 @@ struct log {
 	/* Application identifier used to group syslog messages. */
 	char *syslog_ident;
 	/**
-	 * Used to wake up the main logger thread from a eio thread.
+	 * Counter identifying number of threads executing log_rotate.
+	 * Protected by rotate_mutex as it is accessed from different
+	 * threads.
 	 */
-	ev_async log_async;
-	/**
-	 * Conditional variable securing variable below
-	 * from concurrent usage.
-	 */
-	struct fiber_cond rotate_cond;
-	/** Counter identifying number of threads executing log_rotate. */
 	int rotating_threads;
+	/** Mutex for accessing rotating_threads field. */
+	pthread_mutex_t rotate_mutex;
+	/** Condition that all rotation tasks are finished. */
+	pthread_cond_t rotate_cond;
 	enum syslog_facility syslog_facility;
 	struct rlist in_log_list;
 	/** Callback called on log event. */
diff --git a/test/unit/say.c b/test/unit/say.c
index 49073d409251d4e4ffa35cf832cbb149681e02d4..bfb72d530157e6fca07b3bd1e02f4f9e082ec432 100644
--- a/test/unit/say.c
+++ b/test/unit/say.c
@@ -271,6 +271,13 @@ int main()
 		ok(strstr(line, "<131>") != NULL, "syslog line");
 	}
 	log_destroy(&test_log);
+
+	/* Test gh-4450. */
+	log_create(&test_log, tmp_filename, false);
+	say_logrotate(NULL, NULL, 0);
+	coio_shutdown();
+	log_destroy(&test_log);
+
 	fiber_free();
 	memory_free();
 	unlink(tmp_filename);