Jelajahi Sumber

:recycle: Implement some delayed kernel events using task queues https://github.com/siyuan-note/siyuan/issues/12393

Daniel 10 bulan lalu
induk
melakukan
9560277d0c

+ 7 - 7
kernel/api/av.go

@@ -212,7 +212,7 @@ func addAttributeViewBlocks(c *gin.Context) {
 		return
 	}
 
-	util.PushReloadAttrView(avID)
+	model.ReloadAttrView(avID)
 }
 
 func removeAttributeViewBlocks(c *gin.Context) {
@@ -237,7 +237,7 @@ func removeAttributeViewBlocks(c *gin.Context) {
 		return
 	}
 
-	util.PushReloadAttrView(avID)
+	model.ReloadAttrView(avID)
 }
 
 func addAttributeViewKey(c *gin.Context) {
@@ -263,7 +263,7 @@ func addAttributeViewKey(c *gin.Context) {
 		return
 	}
 
-	util.PushReloadAttrView(avID)
+	model.ReloadAttrView(avID)
 }
 
 func removeAttributeViewKey(c *gin.Context) {
@@ -285,7 +285,7 @@ func removeAttributeViewKey(c *gin.Context) {
 		return
 	}
 
-	util.PushReloadAttrView(avID)
+	model.ReloadAttrView(avID)
 }
 
 func sortAttributeViewViewKey(c *gin.Context) {
@@ -312,7 +312,7 @@ func sortAttributeViewViewKey(c *gin.Context) {
 		return
 	}
 
-	util.PushReloadAttrView(avID)
+	model.ReloadAttrView(avID)
 }
 
 func sortAttributeViewKey(c *gin.Context) {
@@ -335,7 +335,7 @@ func sortAttributeViewKey(c *gin.Context) {
 		return
 	}
 
-	util.PushReloadAttrView(avID)
+	model.ReloadAttrView(avID)
 }
 
 func getAttributeViewFilterSort(c *gin.Context) {
@@ -609,5 +609,5 @@ func setAttributeViewBlockAttr(c *gin.Context) {
 	blockAttributeViewKeys := model.UpdateAttributeViewCell(nil, avID, keyID, rowID, cellID, value)
 	ret.Data = blockAttributeViewKeys
 
-	util.PushReloadAttrView(avID)
+	model.ReloadAttrView(avID)
 }

+ 17 - 7
kernel/model/attribute_view.go

@@ -19,6 +19,7 @@ package model
 import (
 	"bytes"
 	"fmt"
+	"github.com/siyuan-note/siyuan/kernel/task"
 	"os"
 	"path/filepath"
 	"slices"
@@ -92,7 +93,7 @@ func AppendAttributeViewDetachedBlocksWithValues(avID string, blocksValues [][]*
 		return
 	}
 
-	util.PushReloadAttrView(avID)
+	ReloadAttrView(avID)
 	return
 }
 
@@ -1044,7 +1045,7 @@ func unbindAttributeViewBlock(operation *Operation, tx *Transaction) (err error)
 
 	changedAvIDs = gulu.Str.RemoveDuplicatedElem(changedAvIDs)
 	for _, avID := range changedAvIDs {
-		util.PushReloadAttrView(avID)
+		ReloadAttrView(avID)
 	}
 	return
 }
@@ -1313,7 +1314,7 @@ func updateAttributeViewColRelation(operation *Operation) (err error) {
 	}
 	if !isSameAv {
 		err = av.SaveAttributeView(destAv)
-		util.PushReloadAttrView(destAv.ID)
+		ReloadAttrView(destAv.ID)
 	}
 
 	av.UpsertAvBackRel(srcAv.ID, destAv.ID)
@@ -2147,7 +2148,7 @@ func removeAttributeViewBlock(srcIDs []string, avID string, tx *Transaction) (er
 
 	relatedAvIDs := av.GetSrcAvIDs(avID)
 	for _, relatedAvID := range relatedAvIDs {
-		util.PushReloadAttrView(relatedAvID)
+		ReloadAttrView(relatedAvID)
 	}
 
 	err = av.SaveAttributeView(attrView)
@@ -2804,7 +2805,7 @@ func RemoveAttributeViewKey(avID, keyID string) (err error) {
 
 				if destAv != attrView {
 					av.SaveAttributeView(destAv)
-					util.PushReloadAttrView(destAv.ID)
+					ReloadAttrView(destAv.ID)
 				}
 
 				if !destAvRelSrcAv {
@@ -2940,7 +2941,7 @@ func replaceAttributeViewBlock(operation *Operation, tx *Transaction) (err error
 
 	changedAvIDs = gulu.Str.RemoveDuplicatedElem(changedAvIDs)
 	for _, avID := range changedAvIDs {
-		util.PushReloadAttrView(avID)
+		ReloadAttrView(avID)
 	}
 	return
 }
@@ -3182,7 +3183,7 @@ func UpdateAttributeViewCell(tx *Transaction, avID, keyID, rowID, cellID string,
 
 	relatedAvIDs := av.GetSrcAvIDs(avID)
 	for _, relatedAvID := range relatedAvIDs {
-		util.PushReloadAttrView(relatedAvID)
+		ReloadAttrView(relatedAvID)
 	}
 
 	if err = av.SaveAttributeView(attrView); err != nil {
@@ -3579,3 +3580,12 @@ func updateBoundBlockAvsAttribute(avIDs []string) {
 		av.BatchUpsertBlockRel(avNodes)
 	}
 }
+
+func ReloadAttrView(avID string) {
+	task.AppendTaskWithDelay(task.ReloadAttributeView, 200*time.Millisecond, pushReloadAttrView, avID)
+
+}
+
+func pushReloadAttrView(avID string) {
+	util.BroadcastByType("protyle", "refreshAttributeView", 0, "", map[string]interface{}{"id": avID})
+}

+ 1 - 1
kernel/model/file.go

@@ -1113,7 +1113,7 @@ func DuplicateDoc(tree *parse.Tree) {
 				"id":         n.ID,
 				"isDetached": false,
 			}}, avID, "", "", false)
-			util.PushReloadAttrView(avID)
+			ReloadAttrView(avID)
 		}
 		return ast.WalkContinue
 	})

+ 1 - 1
kernel/model/history.go

@@ -312,7 +312,7 @@ func RollbackDocHistory(boxID, historyPath string) (err error) {
 
 		// 刷新属性视图
 		for _, avID := range avIDs {
-			util.PushReloadAttrView(avID)
+			ReloadAttrView(avID)
 		}
 	}()
 	return nil

+ 4 - 4
kernel/model/transaction.go

@@ -813,7 +813,7 @@ func syncDelete2AvBlock(node *ast.Node) {
 	go func() {
 		time.Sleep(256 * time.Millisecond)
 		for _, avID := range changedAvIDs {
-			util.PushReloadAttrView(avID)
+			ReloadAttrView(avID)
 		}
 	}()
 }
@@ -1074,7 +1074,7 @@ func (tx *Transaction) doInsert(operation *Operation) (ret *TxErr) {
 			"id":         insertedNode.ID,
 			"isDetached": false,
 		}}, avID, "", previousID, false)
-		util.PushReloadAttrView(avID)
+		ReloadAttrView(avID)
 	}
 
 	operation.ID = insertedNode.ID
@@ -1187,7 +1187,7 @@ func upsertAvBlockRel(node *ast.Node) {
 	})
 	avIDs = gulu.Str.RemoveDuplicatedElem(avIDs)
 	for _, avID := range avIDs {
-		util.PushReloadAttrView(avID)
+		ReloadAttrView(avID)
 	}
 }
 
@@ -1533,7 +1533,7 @@ func refreshDynamicRefTexts(updatedDefNodes map[string]*ast.Node, updatedTrees m
 			}
 			if changedAv {
 				av.SaveAttributeView(attrView)
-				util.PushReloadAttrView(avID)
+				ReloadAttrView(avID)
 			}
 		}
 	}

+ 74 - 40
kernel/task/queue.go

@@ -37,50 +37,80 @@ type Task struct {
 	Handler reflect.Value
 	Args    []interface{}
 	Created time.Time
+	Delay   time.Duration
 	Timeout time.Duration
 }
 
 func AppendTask(action string, handler interface{}, args ...interface{}) {
-	AppendTaskWithTimeout(action, 24*time.Hour, handler, args...)
+	appendTaskWithDelayTimeout(action, 0, 24*time.Hour, handler, args...)
+}
+
+func AppendTaskWithDelay(action string, delay time.Duration, handler interface{}, args ...interface{}) {
+	appendTaskWithDelayTimeout(action, delay, 24*time.Hour, handler, args...)
 }
 
 func AppendTaskWithTimeout(action string, timeout time.Duration, handler interface{}, args ...interface{}) {
+	appendTaskWithDelayTimeout(action, 0, timeout, handler, args...)
+}
+
+func appendTaskWithDelayTimeout(action string, delay, timeout time.Duration, handler interface{}, args ...interface{}) {
 	if util.IsExiting.Load() {
 		//logging.LogWarnf("task queue is paused, action [%s] will be ignored", action)
 		return
 	}
 
-	currentActions := getCurrentActions()
-	if gulu.Str.Contains(action, currentActions) && gulu.Str.Contains(action, uniqueActions) {
-		//logging.LogWarnf("task [%s] is already in queue, will be ignored", action)
-		return
-	}
-
-	queueLock.Lock()
-	defer queueLock.Unlock()
-	taskQueue = append(taskQueue, &Task{
+	task := &Task{
 		Action:  action,
-		Timeout: timeout,
 		Handler: reflect.ValueOf(handler),
 		Args:    args,
 		Created: time.Now(),
-	})
+		Delay:   delay,
+		Timeout: timeout,
+	}
+
+	if gulu.Str.Contains(action, uniqueActions) {
+		if currentTasks := getCurrentTasks(); containTask(task, currentTasks) {
+			//logging.LogWarnf("task [%s] is already in queue, will be ignored", action)
+			return
+		}
+	}
+
+	queueLock.Lock()
+	defer queueLock.Unlock()
+	taskQueue = append(taskQueue, task)
+}
+
+func containTask(task *Task, tasks []*Task) bool {
+	for _, t := range tasks {
+		if t.Action == task.Action {
+			if len(t.Args) != len(task.Args) {
+				return false
+			}
+
+			for i, arg := range t.Args {
+				if arg != task.Args[i] {
+					return false
+				}
+			}
+			return true
+		}
+	}
+	return false
 }
 
-func getCurrentActions() (ret []string) {
+func getCurrentTasks() (ret []*Task) {
 	queueLock.Lock()
+	defer queueLock.Unlock()
 
-	currentTaskActionLock.Lock()
-	if "" != currentTaskAction {
-		ret = append(ret, currentTaskAction)
+	currentTaskLock.Lock()
+	if nil != currentTask {
+		ret = append(ret, currentTask)
 	}
-	currentTaskActionLock.Unlock()
+	currentTaskLock.Unlock()
 
 	for _, task := range taskQueue {
-		ret = append(ret, task.Action)
+		ret = append(ret, task)
 	}
-
-	queueLock.Unlock()
 	return
 }
 
@@ -100,6 +130,7 @@ const (
 	AssetContentDatabaseIndexFull   = "task.asset.database.index.full"     // 资源文件数据库重建索引
 	AssetContentDatabaseIndexCommit = "task.asset.database.index.commit"   // 资源文件数据库索引提交
 	CacheVirtualBlockRef            = "task.cache.virtualBlockRef"         // 缓存虚拟块引用
+	ReloadAttributeView             = "task.reload.attributeView"          // 重新加载属性视图
 )
 
 // uniqueActions 描述了唯一的任务,即队列中只能存在一个在执行的任务。
@@ -116,9 +147,9 @@ var uniqueActions = []string{
 }
 
 func ContainIndexTask() bool {
-	actions := getCurrentActions()
-	for _, action := range actions {
-		if gulu.Str.Contains(action, []string{DatabaseIndexFull, DatabaseIndex}) {
+	tasks := getCurrentTasks()
+	for _, task := range tasks {
+		if gulu.Str.Contains(task.Action, []string{DatabaseIndexFull, DatabaseIndex}) {
 			return true
 		}
 	}
@@ -150,15 +181,13 @@ func StatusJob() {
 	}
 	defer queueLock.Unlock()
 
-	currentTaskActionLock.Lock()
-	if "" != currentTaskAction {
-		if nil != actionLangs {
-			if label := actionLangs[currentTaskAction]; nil != label {
-				items = append([]map[string]interface{}{{"action": label.(string)}}, items...)
-			}
+	currentTaskLock.Lock()
+	if nil != currentTask && nil != actionLangs {
+		if label := actionLangs[currentTask.Action]; nil != label {
+			items = append([]map[string]interface{}{{"action": label.(string)}}, items...)
 		}
 	}
-	currentTaskActionLock.Unlock()
+	currentTaskLock.Unlock()
 
 	if 1 > len(items) {
 		items = []map[string]interface{}{}
@@ -189,14 +218,19 @@ func popTask() (ret *Task) {
 		return
 	}
 
-	ret = taskQueue[0]
-	taskQueue = taskQueue[1:]
+	for i, task := range taskQueue {
+		if time.Since(task.Created) > task.Delay {
+			ret = task
+			taskQueue = append(taskQueue[:i], taskQueue[i+1:]...)
+			return
+		}
+	}
 	return
 }
 
 var (
-	currentTaskAction     string
-	currentTaskActionLock = sync.Mutex{}
+	currentTask     *Task
+	currentTaskLock = sync.Mutex{}
 )
 
 func execTask(task *Task) {
@@ -211,9 +245,9 @@ func execTask(task *Task) {
 		}
 	}
 
-	currentTaskActionLock.Lock()
-	currentTaskAction = task.Action
-	currentTaskActionLock.Unlock()
+	currentTaskLock.Lock()
+	currentTask = task
+	currentTaskLock.Unlock()
 
 	ctx, cancel := context.WithTimeout(context.Background(), task.Timeout)
 	defer cancel()
@@ -230,7 +264,7 @@ func execTask(task *Task) {
 		//logging.LogInfof("task [%s] done", task.Action)
 	}
 
-	currentTaskActionLock.Lock()
-	currentTaskAction = ""
-	currentTaskActionLock.Unlock()
+	currentTaskLock.Lock()
+	currentTask = nil
+	currentTaskLock.Unlock()
 }

+ 0 - 4
kernel/util/websocket.go

@@ -240,10 +240,6 @@ func PushClearProgress() {
 	BroadcastByType("main", "cprogress", 0, "", nil)
 }
 
-func PushReloadAttrView(avID string) {
-	BroadcastByType("protyle", "refreshAttributeView", 0, "", map[string]interface{}{"id": avID})
-}
-
 func PushReloadDoc(rootID string) {
 	BroadcastByType("main", "reloaddoc", 0, "", rootID)
 }