lazy-list C 代码详解

先从github上可以把相应的Synchrobench代码下载下来https://github.com/gramoli/synchrobench

一.lazy-list代码重构的潜在需求:

  第一个也是最明显的问题是内存回收问题。 比如在并发执行的过程中,如果只是一个节点b的逻辑移除,即使它不再存在于抽象集合中。 但实际上还是有联系的,只是一开始就达不到。 如下所示在这里插入图片描述
这样,如果项目不是空闲的,随着时间的推移,它会导致内存激增。 因此分配给项目节点的内存被回收。
如果你让我去做,我会在最后收回记忆来坚持记忆。

解决方案1:垃圾收集

但是这个方法只来自 Java 和 C#,而不是 C/C++(至少官方)

解决方案 2:智能指针(Smart Pointers)

C++ 及更高版本中的智能指针(#include),它通过三个 API 扮演每个对象的垃圾收集角色和 RAII。 而如果我们只是调用API,就像一个黑匣子,我们只知道结果而不知道过程。

解决方案 3:特定领域的回收

并发数据结构:如惰性链表、无锁链表

二.lazy-list潜在的线程安全问题

  条件变量设置不合理,加锁解锁过程没有设置休眠时间。 如果超时到期,它将无限期地等待。 (没有有效和有用的等待功能)。
  使用全局共享变量容易增加不同任务或线程之间的耦合,也增加了引入bug的风险,所以应该尽量少使用全局共享变量。
  节点是否标记与标记参考情况的功能设计是高度耦合的。 如果实际情况发生变化,修改起来会相当麻烦。 有效性检查和搜索功能是相同的。 插入和删除函数不考虑 while 循环中的异常。 如果中间出现错误,就会陷入死循环。

解决方案:

  1. 通过引用传递而不是传递指针
  2. 尽可能依赖构造函数、复制构造函数和析构函数
  3. 不能只使用堆栈分配

三.lazy-list 代码详解

  1. coupling.c
int lockc_delete(intset_l_t *set, val_t val) {
    node_l_t *curr, *next;
    int found;
    
    //Lock the first two elements
    LOCK(&set->head->lock);
    curr = set->head;
    LOCK(&curr->next->lock);
    next = curr->next;
    
    //如果下一个节点的值<当前值:解锁前一个,保留当前的所有权,并在循环中锁定下一个
    while (next->val < val) {
        UNLOCK(&curr->lock);
        curr = next;
        LOCK(&next->next->lock);
        next = next->next;
    }
    // wait until next node's value == current value
    found = (val == next->val);
    // 'found' = 1, 然后找到下一个节点,同时解锁并删除它。最后,解锁它的下一个节点
    if (found) { 
      curr->next = next->next;
      UNLOCK(&next->lock);
      node_delete_l(next);
      UNLOCK(&curr->lock);
    } else {              // 'found' = 0, only unclock it and its next node 
      UNLOCK(&curr->lock);
      UNLOCK(&next->lock);
    }
    //return whether delete next node
    return found;
}

delete, find, and insert方法也差不多

  1. insert.c
//当 d->unit_tx == 2 时,返回 ((curr->val == val) && !is_marked_ref((long) curr->next)),否则,在获取元素副本之前Locking每个元素
int set_contains_l(intset_l_t *set, val_t val, int transactional)
{
    if (transactional == 2) return parse_find(set, val);
    else return lockc_find(set, val);
}

add和remove方法与此相差不大
3) linkedlist-lock.c

node_l_t *new_node_l(val_t val, node_l_t *next, int transactional){// INIT_LOCK and assign memory to new node
intset_l_t *set_new_l(){//insert node and set min and max for node
void node_delete_l(node_l_t *node) {//DESTROY_LOCK
void set_delete_l(intset_l_t *set){//DESTROY_LOCK and move current node to next
int set_size_l(intset_l_t *set){//compute length of nodes

  1. lazy.c
/*
 * File:
 *   lazy.c
 * Author(s):
 *   Vincent Gramoli <vincent.gramoli@epfl.ch>
 * Description:
 *   Lazy linked list implementation of an integer set based on Heller et al. algorithm
 *   "A Lazy Concurrent List-Based Set Algorithm"
 *   S. Heller, M. Herlihy, V. Luchangco, M. Moir, W.N. Scherer III, N. Shavit
 *   p.3-16, OPODIS 2005
 *
 * Copyright (c) 2009-2010.
 *
 * lazy.c is part of Synchrobench
 * 
 * Synchrobench is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, version 2
 * of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include "lazy.h"

inline int is_marked_ref(long i) {
	return (int) (i &= LONG_MIN+1);
}

inline long unset_mark(long i) {
	i &= LONG_MAX-1;
	return i;
}

inline long set_mark(long i) {
	i = unset_mark(i);
	i += 1;
	return i;
}

inline node_l_t *get_unmarked_ref(node_l_t *n) {
	return (node_l_t *) unset_mark((long) n);
}

inline node_l_t *get_marked_ref(node_l_t *n) {
	return (node_l_t *) set_mark((long) n);
}

/*
 * Checking that both curr and pred are both unmarked and that pred's next pointer
 * points to curr to verify that the entries are adjacent and present in the list.
 */
inline int parse_validate(node_l_t *pred, node_l_t *curr) {
	return (!is_marked_ref((long) pred->next) && !is_marked_ref((long) curr->next) && (pred->next == curr));
}

int parse_find(intset_l_t *set, val_t val) {//wait until curr->val == val, set next node to unmark 
	node_l_t *curr;
	curr = set->head;
	while (curr->val < val)
		curr = get_unmarked_ref(curr->next);
	return ((curr->val == val) && !is_marked_ref((long) curr->next));
}

int parse_insert(intset_l_t *set, val_t val) {
	node_l_t *curr, *pred, *newnode;
	int result, validated, notVal;
	while (1) {
		//Init
		pred = set->head;
		curr = get_unmarked_ref(pred->next);
		//Unmark and lock each node
		while (curr->val < val) {
			pred = curr;
			curr = get_unmarked_ref(curr->next);
		}
		LOCK(&pred->lock);
		LOCK(&curr->lock);
		//check its validated and curr->val whether equals val
		validated = parse_validate(pred, curr);
		notVal = (curr->val != val);
		result = (validated && notVal);
		//only meeting at same time, make pred->next = newnode
		if (result) {
			newnode = new_node_l(val, curr, 0);
			pred->next = newnode;
		} 
		//UNLOCK curr and pred
		UNLOCK(&curr->lock);
		UNLOCK(&pred->lock);
		//wait until pred and curr meet the validate conditions, exit this loop
		if(validated)
			return result;
	}
}

/*
 * Logically remove an element by setting a mark bit to 1 
 * before removing it physically.
 *
 * NB. it is not safe to free the element after physical deletion as a 
 * pre-empted find operation may currently be parsing the element.
 * TODO: must implement a stop-the-world garbage collector to correctly 
 * free the memory.
 */
int parse_delete(intset_l_t *set, val_t val) {
	node_l_t *pred, *curr;
	int result, validated, isVal;
	while(1) {
		//Init
		pred = set->head;
		curr = get_unmarked_ref(pred->next);
		//Unmark and lock each node
		while (curr->val < val) {
			pred = curr;
			curr = get_unmarked_ref(curr->next);
		}
		LOCK(&pred->lock);
		LOCK(&curr->lock);
/*
* Mark Phase: 
* Start from the root set to find all memory block references, and then mark the referenced memory block (mark)
*/
		//check its validated and curr->val whether equals val
		validated = parse_validate(pred, curr);
		isVal = val == curr->val;
		result = validated && isVal;
		//only meeting at same time, make pred->next = newnode
		if (result) {
			curr->next = get_marked_ref(curr->next);
			pred->next = get_unmarked_ref(curr->next);
		}
		//UNLOCK curr and pred
		UNLOCK(&curr->lock);
		UNLOCK(&pred->lock);
/*
* Sweep Phase:
* All memory blocks that are not marked are considered garbage and can be recycled
*/
		//find memory blocks that are not marked, setting a mark bit to 1
		result = result && parse_find(curr,isVal);
		//wait until pred and curr meet the validate conditions, exit this loop
		if(validated){
			//free the element
			free(curr);
			return result;
		}
			
	}
}

//Checking this node whether null
bool isEmpty(intset_l_t *set, val_t val) {

	node_l_t  *curr = set->head;

	//search an element that exist in the list, both logically and physically
	while (curr->val != val) {
			//make sure element was not removed logically
			curr = get_unmarked_ref(curr->next);
	}
	return ((curr->val == val) && !is_marked_ref((long) curr->next));
}


/*Table of sleeping threads. */
static struct array *sleepers;

struct node_l_t *thread_bootstrap(void){
    node_l_t *curr, *pred, *newnode;
    /*Create the data structures we need.*/
    sleepers = array_create();
    if(!sleepers){
        printf("Cannot create sleepers array\n");
    }
}


void node_sleep(intset_l_t *set, val_t val){
    //may not sleep in an interrupt handler
	node_l_t *curr, *pred;

    LOCK(&pred->lock);
	LOCK(&curr->lock);
    mi_switch(S_SLEEP);
    UNLOCK(&curr->lock);
	UNLOCK(&pred->lock);
}

/* Release lock, put node to sleep until cv is signaled; when node wakes up again, re-acquire lock before returning */
void node_wakeup(const void *addr){
    int i, result;
	isVal = val == curr->val;
	node_l_t *curr;
    //This is inefficient, Feel free to improve it.

    for(i=0; i<array_getnum(sleepers); i++){
        node_l_t *n = array_getguy(sleepers, i);
        if (n->val == val) {
            //Remove from list
            array_remove(sleepers. i);

            //must look at the same sleepers[i] again
            i--;
			UNLOCK(&curr->lock);
			curr = curr->next;
            /*
            Because we preallocate during therad_fork,
            this should never fail. 
            */
           result = make_runnable(t);
           assert(result==0);
        }
        
    }
}


  1. lazy.h
/*
 * File:
 *   lazy.c
 * Author(s):
 *   Vincent Gramoli <vincent.gramoli@epfl.ch>
 * Description:
 *   Lazy linked list implementation of an integer set based on Heller et al. algorithm
 *   "A Lazy Concurrent List-Based Set Algorithm"
 *   S. Heller, M. Herlihy, V. Luchangco, M. Moir, W.N. Scherer III, N. Shavit
 *   p.3-16, OPODIS 2005
 *
 * Copyright (c) 2009-2010.
 *
 * lazy.c is part of Synchrobench
 * 
 * Synchrobench is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, version 2
 * of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include "coupling.h"

/* handling logical deletion flag */ 
inline int is_marked_ref(long i);
inline long unset_mark(long i);
inline long set_mark(long i);
inline node_l_t *get_unmarked_ref(node_l_t *n);
inline node_l_t *get_marked_ref(node_l_t *n);

/* linked list accesses */
int parse_validate(node_l_t *pred, node_l_t *curr);
int parse_find(intset_l_t *set, val_t val);
int parse_insert(intset_l_t *set, val_t val);
int parse_delete(intset_l_t *set, val_t val);

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值