package concurrency

import (
    "fmt"
    "sync"

    v3 "github.com/coreos/etcd/clientv3"
    "golang.org/x/net/context"
)

// Mutex implements the sync Locker interface with etcd
type Mutex struct {
    s *Session

    pfx   string
    myKey string
    myRev int64
}

func NewMutex(s *Session, pfx string) *Mutex {
    return &Mutex{s, pfx + "/", "", -1}
}

// Lock locks the mutex with a cancellable context. If the context is cancelled
// while trying to acquire the lock, the mutex tries to clean its stale lock entry.
func (m *Mutex) Lock(ctx context.Context) error {
    s := m.s
    client := m.s.Client()

    m.myKey = fmt.Sprintf("%s%x", m.pfx, s.Lease())
    cmp := v3.Compare(v3.CreateRevision(m.myKey), "=", 0)
    // put self in lock waiters via myKey; oldest waiter holds lock
    put := v3.OpPut(m.myKey, "", v3.WithLease(s.Lease()))
    // reuse key in case this session already holds the lock
    get := v3.OpGet(m.myKey)
    resp, err := client.Txn(ctx).If(cmp).Then(put).Else(get).Commit()
    if err != nil {
        return err
    }
    m.myRev = resp.Header.Revision
    if !resp.Succeeded {
        m.myRev = resp.Responses[0].GetResponseRange().Kvs[0].CreateRevision
    }

    // wait for deletion revisions prior to myKey
    err = waitDeletes(ctx, client, m.pfx, m.myRev-1)
    // release lock key if cancelled
    select {
    case <-ctx.Done():
        m.Unlock(client.Ctx())
    default:
    }
    return err
}

func (m *Mutex) Unlock(ctx context.Context) error {
    client := m.s.Client()
    if _, err := client.Delete(ctx, m.myKey); err != nil {
        return err
    }
    m.myKey = "\x00"
    m.myRev = -1
    return nil
}

func (m *Mutex) IsOwner() v3.Cmp {
    return v3.Compare(v3.CreateRevision(m.myKey), "=", m.myRev)
}

func (m *Mutex) Key() string { return m.myKey }

type lockerMutex struct{ *Mutex }

func (lm *lockerMutex) Lock() {
    client := lm.s.Client()
    if err := lm.Mutex.Lock(client.Ctx()); err != nil {
        panic(err)
    }
}
func (lm *lockerMutex) Unlock() {
    client := lm.s.Client()
    if err := lm.Mutex.Unlock(client.Ctx()); err != nil {
        panic(err)
    }
}

// NewLocker creates a sync.Locker backed by an etcd mutex.
func NewLocker(s *Session, pfx string) sync.Locker {
    return &lockerMutex{NewMutex(s, pfx)}
}

mutex.go的更多相关文章

  1. C#各种同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEvent

    看下组织结构: System.Object System.MarshalByRefObject System.Threading.WaitHandle System.Threading.Mutex S ...

  2. std::unique_lock<std::mutex> or std::lock_guard<std::mutex> C++11 区别

    http://stackoverflow.com/questions/20516773/stdunique-lockstdmutex-or-stdlock-guardstdmutex The diff ...

  3. WPF整理-Mutex确保Application单例运行

    有时我们不希望我们的WPF应用程序可以同时运行有多个实例,当我们试图运行第二个实例的时候,已经运行的实例也应该弹出来. 我们可以用Mutex来实现 打开App.xaml.cs,在App类中添加如下内容 ...

  4. C#互斥体——Mutex

    Mutex对象是一个同步基元,可以用来做线程间的同步. 若多个线程需要共享一个资源,可以在这些线程中使用Mutex同步基元.当某一个线程占用Mutex对象时,其他也需要占用Mutex的线程将处于挂起状 ...

  5. 多线程相关------互斥量Mutex

    互斥量(Mutex) 互斥量是一个可以处于两态之一的变量:解锁和加锁.只有拥有互斥对象的线程才具有访问资源的权限.并且互斥量可以用于不同进程中的线程的互斥访问. 相关函数: CreateMutex用于 ...

  6. 锁相关知识 & mutex怎么实现的 & spinlock怎么用的 & 怎样避免死锁 & 内核同步机制 & 读写锁

    spinlock在上一篇文章有提到:http://www.cnblogs.com/charlesblc/p/6254437.html  通过锁数据总线来实现. 而看了这篇文章说明:mutex内部也用到 ...

  7. Mutex 和 Lock

    #include <future> #include <mutex> #include <iostream> #include <string> #in ...

  8. channel vs mutex

    记录许总演讲PPT指出的实践: channel– 本质上是一个 MessageQueue– 非常正统的执行体间通讯设施• sync.Mutex/RWMutex/Cond/etc– 不要把 channe ...

  9. go sync.Mutex 设计思想与演化过程 (一)

    go语言在云计算时代将会如日中天,还抱着.NET不放的人将会被淘汰.学习go语言和.NET完全不一样,它有非常简单的runtime 和 类库.最好的办法就是将整个源代码读一遍,这是我见过最简洁的系统类 ...

  10. 一次Mutex死锁的原因探究

    1.现象    最近项目中调出一个bug,某些时候程序会卡死不动,用windbg进行加载后用 ~*kb 命令列出所有的线程栈调用,发现有多个线程调用 WaitForMultipleObjects 在等 ...

随机推荐

  1. .net 异步编程async & await关键字的思考

    C# 5.0引入了两个关键字 async和await,这两个关键字在很大程度上帮助我们简化了异步编程的实现代码,而且TPL中的task与async和await有很大的关系 思考了一下异步编程中的asy ...

  2. 关于Intent

    TCP/IP 协议是Internet国际网络的基础,主要包括TCP,IP,UDP和ICMP等协议 要连上internet,操作系统必须安装TCP/IP协议,TCP/IP协议可以让不同类型,不同操作系统 ...

  3. spring boot + quartz 集群

    spring boot bean配置: @Configuration public class QuartzConfig { @Value("${quartz.scheduler.insta ...

  4. reorder list(链表重新排序)

    Given a singly linked list L: L0→L1→-→Ln-1→Ln,reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→- You must do thi ...

  5. linux安装VLAN,系统怎么划分VLAN打标签上交换机

    前几天公司一台物理机需要连接公网,但是公网需要网卡打标签上去. 由于没有做过linux主机划分VLAN的操作,因此去查了一下,需要利用vconfig这个命令. 但是纠结的是,系统源中没有这个包.(很坑 ...

  6. Redis实现简单消息队列

    http://www.jianshu.com/p/9c04890615ba 任务异步化 打开浏览器,输入地址,按下回车,打开了页面.于是一个HTTP请求(request)就由客户端发送到服务器,服务器 ...

  7. 爬虫初探--PHP

    我有收藏的cms网站,偶尔会下载一些资源,老司机都懂的:-D.然后有一次好几天没上,堆了好些没弄,心想:cao,这好麻烦啊,能不能写个脚本自动帮我搞?然后忽然就想到,这是不是就是所谓的爬虫呢?心中一阵 ...

  8. 从 <sofa:XXX> 标签开始看 SOFA-Boot 如何融入 Spring

    前言 SOFA-Boot 现阶段支持 XML 的方式在 Spring 中定义 Bean,通过这些标签,我们就能从 Spring 容器中取出 RPC 中的引用,并进行调用,那么他是如何处理这些自定义标签 ...

  9. vue.js中的全局组件和局部组件

    组件(Component)是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能. 组件的使用有三 ...

  10. 为什么在JDBC要使用Class.forName();这句话

    为什么在调用JDBC的时候,我们总要写这句话:Class.forName("驱动类");解释:在JDBC编程中一般有以下几个步骤:1>加载驱动,也就是Class.forNam ...