Bug #70417 rw_lock_x_lock_func_nowait() calls os_thread_get_curr_id() mostly needlessly
Submitted: 25 Sep 2013 11:04 Modified: 3 Feb 2014 10:48
Reporter: Laurynas Biveinis (OCA) Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S5 (Performance)
Version:5.6 OS:Any
Assigned to: CPU Architecture:Any

[25 Sep 2013 11:04] Laurynas Biveinis
Description:
ibool
rw_lock_x_lock_func_nowait(...)
{
	os_thread_id_t	curr_thread	= os_thread_get_curr_id();

...attempt to acquire lock by CAS...

	if (success) {
		rw_lock_set_writer_id_and_recursion_flag(lock, TRUE);

	} else if (lock->recursive
		   && os_thread_eq(lock->writer_thread, curr_thread)) {
...
	} else {
...
	}
...
}

Thus, curr_thread is only needed if we failed to acquire the lock and it's a recursive lock. os_thread_get_curr_id() is not an inline function, thus it's a call preceding the CAS.  And we saw such callstacks in PMP, obviously not in the top positions but still.

How to repeat:
Code analysis, or when you run enough PMP you will see

1 os_thread_get_curr_id(os0thread.cc:98),rw_lock_x_lock_func(sync0rw.cc:1146),pfs_rw_lock_x_lock_func(sync0rw.ic:883),buf_page_init_for_read(sync0rw.ic:883),buf_read_page_low(buf0rea.cc:168),buf_read_page(buf0rea.cc:454),buf_page_get_gen(buf0buf.cc:2698),btr_cur_search_to_nth_level(btr0cur.cc:759),row_ins_clust_index_entry_low(row0ins.cc:2370),row_ins_clust_index_entry(row0ins.cc:2909),row_ins_index_entry(row0ins.cc:2909),row_ins_index_entry_step(row0ins.cc:2909),row_ins(row0ins.cc:2909),row_ins_step(row0ins.cc:2909),row_insert_for_mysql(row0mysql.cc:1315),ha_innobase::write_row(ha_innodb.cc:6922),handler::ha_write_row(handler.cc:7429),ha_partition::write_row(ha_partition.cc:3956),handler::ha_write_row(handler.cc:7429),write_record(sql_insert.cc:1668),mysql_insert(sql_insert.cc:1073),mysql_execute_command(sql_parse.cc:3647),mysql_parse(sql_parse.cc:6477),dispatch_command(sql_parse.cc:1355),do_handle_one_connection(sql_connect.cc:1615),handle_one_connection(sql_connect.cc:1526),start_thread(libpthread.so.0),clone(libc.so.6)

Suggested fix:
After inlining curr_thread variable so that os_thread_get_curr_id() is called in else if only, we never saw such stacktrace again.
[25 Sep 2013 15:25] MySQL Verification Team
thanks, verified in 5.7 also
[27 Nov 2013 17:49] Daniel Price
Fixed as of 5.6.16, 5.7.4. Noted in 5.6.16, 5.7.4 changelogs:

In "sync0rw.ic", "rw_lock_x_lock_func_nowait" would needlessly call
"os_thread_get_curr_id".
[3 Feb 2014 10:48] Laurynas Biveinis
5.6$ bzr log -r 5655
------------------------------------------------------------
revno: 5655
committer: Aditya A <aditya.a@oracle.com>
branch nick: mysql-5.6
timestamp: Wed 2013-11-27 20:09:55 +0530
message:
  Bug#17509710	 RW_LOCK_X_LOCK_FUNC_NOWAIT() CALLS 
                   OS_THREAD_GET_CURR_ID() MOSTLY NEEDLESSLY
  
  PROBLEM
  -------
  os_thread_get_curr_id() was being called for each 
  invocation of function rw_lock_x_lock_func_nowait(),
  but only used when we fail to acquire the locks.
  
  FIX
  ---
  Call os_thread_get_curr_id() only when required. 
  
  [Approved by Marko #rb 3967]